Skip to main content

strat9_bus_drivers/
moxtet.rs

1use crate::{BusChild, BusDriver, BusError, PowerState};
2use alloc::{string::String, vec::Vec};
3
4const MAX_MODULES: usize = 6;
5
6const COMPATIBLE: &[&str] = &["cznic,moxtet"];
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum MoxtetModuleId {
10    Sfp,
11    Pci,
12    Topaz,
13    Peridot,
14    Usb3,
15    PcieBridge,
16    Unknown(u8),
17}
18
19impl MoxtetModuleId {
20    /// Builds this from raw.
21    pub fn from_raw(raw: u8) -> Self {
22        match raw & 0x0F {
23            0x01 => MoxtetModuleId::Sfp,
24            0x02 => MoxtetModuleId::Pci,
25            0x03 => MoxtetModuleId::Topaz,
26            0x04 => MoxtetModuleId::Peridot,
27            0x05 => MoxtetModuleId::Usb3,
28            0x06 => MoxtetModuleId::PcieBridge,
29            other => MoxtetModuleId::Unknown(other),
30        }
31    }
32
33    /// Performs the name operation.
34    pub fn name(&self) -> &'static str {
35        match self {
36            MoxtetModuleId::Sfp => "sfp",
37            MoxtetModuleId::Pci => "pci",
38            MoxtetModuleId::Topaz => "topaz",
39            MoxtetModuleId::Peridot => "peridot",
40            MoxtetModuleId::Usb3 => "usb3",
41            MoxtetModuleId::PcieBridge => "pcie-bridge",
42            MoxtetModuleId::Unknown(_) => "unknown",
43        }
44    }
45}
46
47/// A module discovered on the moxtet SPI shift chain.
48pub struct MoxtetModule {
49    pub id: MoxtetModuleId,
50    /// Zero-based position of this module on the chain. This is the value
51    /// used to address the module through `BusDriver::read_reg`/
52    /// `write_reg` : see [`Moxtet`] for the addressing scheme.
53    pub index: u8,
54}
55
56/// CZ.NIC Turris Omnia moxtet bus driver.
57///
58/// # Register-space semantics (`/bus/moxtet/reg/<idx>`)
59///
60/// The moxtet bus has **no MMIO register file**: it is a software-defined
61/// FPGA bus where modules are enumerated at discovery time over SPI and
62/// addressed by their position on the shift chain. Consequently, the
63/// `offset` argument of [`BusDriver::read_reg`]/[`BusDriver::write_reg`]
64/// (and therefore the `/bus/moxtet/reg/<idx>` scheme paths) is a
65/// **zero-based module index**, *not* a byte/hex MMIO offset:
66///
67/// - `reg/0` reads/writes the TX/RX buffer slot of module 0,
68/// - offsets `>= module_count` are rejected with [`BusError::DeviceNotFound`].
69///
70/// This deviates from MMIO-mapped buses where `reg/<hex>` is a hardware
71/// offset; it is kept for compatibility with the [`BusDriver`] trait API.
72pub struct Moxtet {
73    modules: Vec<MoxtetModule>,
74    module_count: usize,
75    tx_buf: [u8; MAX_MODULES + 1],
76    rx_buf: [u8; MAX_MODULES + 1],
77    irq_mask: [bool; MAX_MODULES],
78    power_state: PowerState,
79}
80
81impl Moxtet {
82    /// Creates a new instance.
83    pub fn new() -> Self {
84        Self {
85            modules: Vec::new(),
86            module_count: 0,
87            tx_buf: [0; MAX_MODULES + 1],
88            rx_buf: [0; MAX_MODULES + 1],
89            irq_mask: [false; MAX_MODULES],
90            power_state: PowerState::Off,
91        }
92    }
93
94    /// Performs the discover topology operation.
95    pub fn discover_topology(&mut self, spi_data: &[u8]) {
96        self.modules.clear();
97        self.module_count = 0;
98
99        for (i, &byte) in spi_data.iter().enumerate().skip(1) {
100            if i > MAX_MODULES {
101                break;
102            }
103            let id = MoxtetModuleId::from_raw(byte);
104            self.modules.push(MoxtetModule {
105                id,
106                index: (i - 1) as u8,
107            });
108            self.module_count = i;
109        }
110    }
111
112    /// Performs the module read operation.
113    pub fn module_read(&self, index: usize) -> Result<u8, BusError> {
114        if index >= self.module_count {
115            return Err(BusError::DeviceNotFound);
116        }
117        Ok(self.rx_buf[index + 1])
118    }
119
120    /// Performs the module write operation.
121    pub fn module_write(&mut self, index: usize, value: u8) -> Result<(), BusError> {
122        if index >= self.module_count {
123            return Err(BusError::DeviceNotFound);
124        }
125        self.tx_buf[index + 1] = value;
126        Ok(())
127    }
128
129    /// Sets irq mask.
130    pub fn set_irq_mask(&mut self, index: usize, masked: bool) {
131        if index < MAX_MODULES {
132            self.irq_mask[index] = masked;
133        }
134    }
135}
136
137impl BusDriver for Moxtet {
138    /// Performs the name operation.
139    fn name(&self) -> &str {
140        "moxtet"
141    }
142
143    /// Performs the compatible operation.
144    fn compatible(&self) -> &[&str] {
145        COMPATIBLE
146    }
147
148    /// Requires explicit SPI backend configuration; no auto-detect.
149    fn probe(&self) -> bool {
150        false
151    }
152
153    /// Performs the init operation.
154    fn init(&mut self, _base: usize) -> Result<(), BusError> {
155        self.power_state = PowerState::On;
156        Ok(())
157    }
158
159    /// Performs the shutdown operation.
160    fn shutdown(&mut self) -> Result<(), BusError> {
161        self.power_state = PowerState::Off;
162        Ok(())
163    }
164
165    /// Reads the RX buffer slot of the module at `offset`.
166    ///
167    /// `offset` is a **module index** (0-based position on the shift
168    /// chain), not an MMIO byte offset : see the [`Moxtet`] type docs.
169    fn read_reg(&self, offset: usize) -> Result<u32, BusError> {
170        self.module_read(offset).map(|v| v as u32)
171    }
172
173    /// Writes the TX buffer slot of the module at `offset`.
174    ///
175    /// `offset` is a **module index** (0-based position on the shift
176    /// chain), not an MMIO byte offset : see the [`Moxtet`] type docs.
177    /// Only the low 8 bits of `value` are significant: each module slot
178    /// is one byte wide in the SPI frame.
179    fn write_reg(&mut self, offset: usize, value: u32) -> Result<(), BusError> {
180        self.module_write(offset, value as u8)
181    }
182
183    /// Performs the children operation.
184    fn children(&self) -> Vec<BusChild> {
185        self.modules
186            .iter()
187            .map(|m| BusChild {
188                name: String::from(m.id.name()),
189                base_addr: m.index as u64,
190                size: 1,
191            })
192            .collect()
193    }
194
195    /// Handles irq.
196    fn handle_irq(&mut self) -> bool {
197        for i in 0..self.module_count {
198            if self.irq_mask[i] {
199                continue;
200            }
201            let status = self.rx_buf[i + 1];
202            if status & 0xF0 != 0 {
203                return true;
204            }
205        }
206        false
207    }
208}