mycelium_pci/
register.rs

1//! A PCI device's [`Status`] and [`Command`] registers.
2use mycelium_bitfield::{bitfield, FromBits};
3
4bitfield! {
5    /// A PCI device's `Status` register.
6    ///
7    /// See <https://wiki.osdev.org/Pci#Status_Register>
8    #[derive(Eq, PartialEq)]
9    pub struct Status<u16> {
10        const _RES0 = 2;
11        /// Interrupt Status.
12        ///
13        /// Represents the state of the device's `INTx#` signal. If set to 1 and
14        /// bit 10 of the [`Command`] register ([`INTERRUPT_DISABLE`]) is set to
15        /// 0, the signal will be asserted; otherwise, the signal will be ignored.
16        ///
17        /// [`INTERUPT_DISABLE`]: Command::INTERRUPT_DISABLE
18        pub const INTERRUPT_STATUS: bool;
19
20        /// Capabilities List
21        ///
22        /// If set to 1, the device implements the pointer for a New Capabilities
23        /// linked list at offset `0x34`; otherwise, the linked list is not available.
24        pub const CAPABILITIES_LIST: bool;
25
26        /// 66 MHz Capable
27        ///
28        /// If set to 1 the device is capable of running at 66 MHz; otherwise,
29        /// the device runs at 33 MHz.
30        pub const IS_66MHZ_CAPABLE: bool;
31
32        /// As of revision 3.0 of the PCI Local Bus specification this bit is
33        /// reserved. In revision 2.1 of the specification this bit was used to
34        /// indicate whether or not a device supported User Definable Features
35        const _RES1 = 1;
36
37        /// Fast Back-To-Back Capable
38        ///
39        /// If set to 1 the device can accept fast back-to-back transactions
40        /// that are not from the same agent; otherwise, transactions can only
41        /// be accepted from the same agent.
42        pub const FAST_BACK_TO_BACK_CAPABLE: bool;
43
44        /// Master Data Parity Error
45        ///
46        /// This bit is only set when the following conditions are met. The bus
47        /// agent asserted `PERR#` on a read or observed an assertion of `PERR#`
48        /// on a write, the agent setting the bit  acted as the bus master for
49        /// the operation in which the error occurred, and bit 6 of the
50        /// [`Command`] register ([`PARITY_ERROR_RESPONSE`] bit) is set to 1.
51        ///
52        /// [`PARITY_ERROR_RESPONSE`]: Command::PARITY_ERROR_RESPONSE
53        pub const MASTER_DATA_PARITY_ERROR: bool;
54
55        /// `DEVSEL#` Timing
56        ///
57        /// Read-only bits that represent the slowest time that a device will
58        /// assert `DEVSEL#` for any bus command except Configuration Space read
59        /// and writes. A value of 0x0 represents fast timing, a value of
60        /// 0x1 represents medium timing, and a value of 0x2 represents slow
61        /// timing.
62        pub const DEVSEL_TIMING: DevselTiming;
63
64        /// Signalled Target Abort
65        ///
66        /// This bit will be set to 1 whenever a target device terminates a
67        /// transaction with Target-Abort.
68        pub const SIGNALLED_TARGET_ABORT: bool;
69
70        /// Received Target Abort
71        ///
72        /// This bit will be set to 1, by a master device, whenever its
73        /// transaction is terminated with Target-Abort.
74        pub const RECEIVED_TARGET_ABORT: bool;
75
76        /// Received Master Abort
77        ///
78        /// This bit will be set to 1, by a master device, whenever its
79        /// transaction (except for Special Cycle transactions) is terminated
80        /// with Master-Abort.
81        pub const RECEIVED_MASTER_ABORT: bool;
82
83        /// Signalled System Error
84        ///
85        /// This bit will be set to 1 whenever the device asserts `SERR#`.
86        pub const SIGNALLED_SYSTEM_ERROR: bool;
87
88        /// Detected Parity Error
89        ///
90        /// This bit will be set to 1 whenever the device detects a parity
91        /// error, even if parity error handling is disabled.
92        pub const DETECTED_PARITY_ERROR: bool;
93    }
94}
95
96bitfield! {
97
98    /// A PCI device's `Command` register.
99    ///
100    /// See <https://wiki.osdev.org/Pci#Command_Register>
101    #[derive(Eq, PartialEq)]
102    pub struct Command<u16> {
103        /// I/O Space Enabled
104        ///
105        /// If set to 1 the device can respond to I/O Space accesses; otherwise,
106        /// the device's response is disabled.
107        pub const IO_SPACE_ENABLED: bool;
108
109        /// Memory Space Enabled
110        ///
111        /// If set to 1 the device can respond to Memory Space accesses;
112        /// otherwise, the device's response is disabled.
113        pub const MEMORY_SPACE_ENABLED: bool;
114
115        /// Bus Master
116        ///
117        /// If set to 1, the device can behave as a bus master; otherwise, the
118        /// device can not generate PCI accesses.
119        pub const BUS_MASTER: bool;
120
121        /// Special Cycle Enabled
122        ///
123        /// If set to 1 the device can monitor Special Cycle operations;
124        /// otherwise, the device will ignore them.
125        pub const SPECIAL_CYCLE_ENABLE: bool;
126
127        /// Memory Write and Invalidate Enabled
128        ///
129        /// If set to 1 the device can generate the Memory Write and Invalidate
130        /// command; otherwise, the Memory Write command must be used.
131        pub const MEMORY_WRITE_AND_INVALIDATE_ENABLED: bool;
132
133        /// VGA Palette Snoop
134        ///
135        /// If set to 1 the device does not respond to palette register writes
136        /// and will snoop the data; otherwise, the device will treat palette
137        /// write accesses like all other accesses.
138        pub const VGA_PALETTE_SNOOP: bool;
139
140        /// Parity Error Response enabled
141        ///
142        /// If set to 1 the device will take its normal action when a parity
143        /// error is detected; otherwise, when an error is detected, the device
144        /// will set bit 15 of the [`Status`] register
145        /// ([`DETECTED_PARITY_ERROR`]), but will not assert the PERR# (Parity
146        /// Error) pin and will continue operation as normal.
147        ///
148        /// [`DETECTED_PARITY_ERROR`]: Status::DETECTED_PARITY_ERROR
149        pub const PARITY_ERROR_RESPONSE_ENABLED: bool;
150
151        /// As of revision 3.0 of the PCI local bus specification this bit is
152        /// hardwired to 0. In earlier versions of the specification this bit
153        /// was used by devices and may have been hardwired to 0, 1, or
154        /// implemented as a read/write bit.
155        const _RES0 = 1;
156
157        /// `SERR#` Enabled
158        ///
159        /// If set to 1 the `SERR#` driver is enabled; otherwise, the driver is disabled.
160        pub const SERR_ENABLED: bool;
161
162        /// Fast Back-To-Back Enabled
163        ///
164        /// If set to 1, indicates a device is allowed to generate fast
165        /// back-to-back transactions; otherwise, fast back-to-back
166        /// transactions are only allowed to the same agent.
167        pub const FAST_BACK_TO_BACK_ENABLED: bool;
168
169        /// Interrupt Disable
170        ///
171        /// If set to 1, the assertion of the devices `INTx#` signal is
172        /// disabled; otherwise, assertion of the signal is enabled.
173        pub const INTERRUPT_DISABLE: bool;
174    }
175}
176
177bitfield! {
178    /// Built-In Self Test (BIST) register.
179    #[derive(Eq, PartialEq)]
180    pub struct Bist<u8> {
181        /// The completion code set by running a built-in self test.
182        ///
183        /// If the test completed successfully, this should be 0.
184        pub const COMPLETION_CODE = 3;
185
186        /// Reserved
187        const _RES = 2;
188
189        /// Start BIST
190        ///
191        /// Set to 1 by the OS to start a BIST. This bit is reset when BIST
192        /// completes. If BIST does not complete after 2 seconds the device
193        /// should be failed by system software.
194        pub const START_BIST: bool;
195        /// BIST Capable
196        ///
197        /// If this is 1, the device supports BIST. If it is 0, this device does
198        /// not support a built-in self test.
199        pub const BIST_CAPABLE: bool;
200    }
201}
202
203impl Bist {
204    /// Returns the device's BIST completion code, if a BIST has completed.
205    ///
206    /// If the BIST is still in progress, this method returns `None`.
207    pub fn completion_code(self) -> Option<u8> {
208        if self.get(Self::START_BIST) {
209            return None;
210        }
211
212        Some(self.get(Self::COMPLETION_CODE))
213    }
214}
215
216/// Slowest time that a device will assert `DEVSEL#` for any bus command except
217/// Configuration Space reads/writes.
218#[derive(Clone, Debug, Eq, PartialEq)]
219#[repr(u8)]
220pub enum DevselTiming {
221    Fast = 0x0,
222    Medium = 0x1,
223    Slow = 0x2,
224}
225
226bitfield! {
227    pub(crate) struct RegisterWord<u32> {
228        pub(crate) const COMMAND: Command;
229        pub(crate) const STATUS: Status;
230    }
231}
232
233// === impl Status ===
234
235impl Status {
236    /// Returns `true` if this device received an abort (either a Master Abort
237    /// or Target Abort).
238    ///
239    /// This returns `true` if the [`Status::RECEIVED_MASTER_ABORT`] or
240    /// [`Status::RECEIVED_TARGET_ABORT`] bits are set.
241    pub fn received_abort(self) -> bool {
242        self.get(Self::RECEIVED_MASTER_ABORT) || self.get(Self::RECEIVED_TARGET_ABORT)
243    }
244}
245
246impl FromBits<u32> for Status {
247    const BITS: u32 = 16;
248    type Error = core::convert::Infallible;
249
250    fn try_from_bits(bits: u32) -> Result<Self, Self::Error> {
251        Ok(Self::from_bits((bits >> 16) as u16))
252    }
253
254    fn into_bits(self) -> u32 {
255        (self.bits() as u32) << 16
256    }
257}
258
259// === impl Command ===
260
261impl FromBits<u32> for Command {
262    const BITS: u32 = 16;
263    type Error = core::convert::Infallible;
264
265    fn try_from_bits(bits: u32) -> Result<Self, Self::Error> {
266        Ok(Self::from_bits(bits as u16))
267    }
268
269    fn into_bits(self) -> u32 {
270        self.bits() as u32
271    }
272}
273
274// === impl DevselTiming ===
275
276impl FromBits<u16> for DevselTiming {
277    const BITS: u32 = 2;
278    type Error = crate::error::UnexpectedValue<u16>;
279
280    fn try_from_bits(bits: u16) -> Result<Self, Self::Error> {
281        match bits as u8 {
282            bits if bits == Self::Fast as u8 => Ok(Self::Fast),
283            bits if bits == Self::Medium as u8 => Ok(Self::Medium),
284            bits if bits == Self::Slow as u8 => Ok(Self::Slow),
285            _ => Err(crate::error::unexpected(bits).named("DEVSEL timing")),
286        }
287    }
288
289    fn into_bits(self) -> u16 {
290        self as u16
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use super::*;
297
298    #[test]
299    fn status_is_valid() {
300        Status::assert_valid();
301    }
302
303    #[test]
304    fn command_is_valid() {
305        Status::assert_valid();
306    }
307
308    #[test]
309    fn register_word_is_valid() {
310        RegisterWord::assert_valid();
311    }
312}