Skip to main content

strat9_bus_drivers/
mips_cdmm.rs

1use crate::{BusChild, BusDriver, BusError, PowerState, mmio::MmioRegion};
2use alloc::{string::String, vec::Vec};
3
4const CDMM_DRB_SIZE: usize = 64;
5
6const CDMM_ACSR_DEVTYPE_SHIFT: u32 = 24;
7const CDMM_ACSR_DEVTYPE_MASK: u32 = 0xFF << CDMM_ACSR_DEVTYPE_SHIFT;
8const CDMM_ACSR_DEVSIZE_SHIFT: u32 = 16;
9const CDMM_ACSR_DEVSIZE_MASK: u32 = 0x1F << CDMM_ACSR_DEVSIZE_SHIFT;
10const CDMM_ACSR_DEVREV_SHIFT: u32 = 12;
11const CDMM_ACSR_DEVREV_MASK: u32 = 0xF << CDMM_ACSR_DEVREV_SHIFT;
12const CDMM_ACSR_UW: u32 = 1 << 3;
13const CDMM_ACSR_UR: u32 = 1 << 2;
14const CDMM_ACSR_SW: u32 = 1 << 1;
15const CDMM_ACSR_SR: u32 = 1 << 0;
16
17const MAX_DRBS: usize = 256;
18
19const COMPATIBLE: &[&str] = &["mti,mips-cdmm"];
20
21#[derive(Debug, Clone)]
22pub struct CdmmDevice {
23    pub dev_type: u8,
24    pub dev_size: u8,
25    pub dev_rev: u8,
26    pub drb_offset: usize,
27    pub user_write: bool,
28    pub user_read: bool,
29    pub super_write: bool,
30    pub super_read: bool,
31}
32
33pub struct MipsCdmm {
34    regs: MmioRegion,
35    devices: Vec<CdmmDevice>,
36    total_drbs: usize,
37    power_state: PowerState,
38}
39
40impl MipsCdmm {
41    /// Creates a new instance.
42    pub fn new() -> Self {
43        Self {
44            regs: MmioRegion::new(),
45            devices: Vec::new(),
46            total_drbs: 0,
47            power_state: PowerState::Off,
48        }
49    }
50
51    /// Performs the discover devices operation.
52    pub fn discover_devices(&mut self) {
53        self.devices.clear();
54        if !self.regs.is_valid() {
55            return;
56        }
57
58        let mut drb = 0;
59        while drb < MAX_DRBS {
60            let offset = drb * CDMM_DRB_SIZE;
61            let acsr = self.regs.read32(offset);
62
63            let dev_type = ((acsr & CDMM_ACSR_DEVTYPE_MASK) >> CDMM_ACSR_DEVTYPE_SHIFT) as u8;
64            let dev_size = ((acsr & CDMM_ACSR_DEVSIZE_MASK) >> CDMM_ACSR_DEVSIZE_SHIFT) as u8;
65            let dev_rev = ((acsr & CDMM_ACSR_DEVREV_MASK) >> CDMM_ACSR_DEVREV_SHIFT) as u8;
66
67            if dev_type == 0 {
68                break;
69            }
70
71            let block_count = if dev_size == 0 { 1 } else { dev_size as usize };
72
73            self.devices.push(CdmmDevice {
74                dev_type,
75                dev_size,
76                dev_rev,
77                drb_offset: offset,
78                user_write: (acsr & CDMM_ACSR_UW) != 0,
79                user_read: (acsr & CDMM_ACSR_UR) != 0,
80                super_write: (acsr & CDMM_ACSR_SW) != 0,
81                super_read: (acsr & CDMM_ACSR_SR) != 0,
82            });
83
84            drb += block_count;
85        }
86
87        self.total_drbs = drb;
88    }
89
90    /// Performs the device read32 operation.
91    pub fn device_read32(&self, dev_index: usize, offset: usize) -> Result<u32, BusError> {
92        let dev = self
93            .devices
94            .get(dev_index)
95            .ok_or(BusError::DeviceNotFound)?;
96        // Validate the composed address before touching MMIO: `offset` is
97        // caller-supplied and drb_offset + offset could otherwise trip the
98        // panic backstop in MmioRegion.
99        self.regs.check_user_offset(dev.drb_offset + offset)?;
100        Ok(self.regs.read32(dev.drb_offset + offset))
101    }
102
103    /// Performs the device write32 operation.
104    pub fn device_write32(
105        &self,
106        dev_index: usize,
107        offset: usize,
108        val: u32,
109    ) -> Result<(), BusError> {
110        let dev = self
111            .devices
112            .get(dev_index)
113            .ok_or(BusError::DeviceNotFound)?;
114        self.regs.check_user_offset(dev.drb_offset + offset)?;
115        self.regs.write32(dev.drb_offset + offset, val);
116        Ok(())
117    }
118}
119
120impl BusDriver for MipsCdmm {
121    /// Performs the name operation.
122    fn name(&self) -> &str {
123        "mips-cdmm"
124    }
125
126    /// Performs the compatible operation.
127    fn compatible(&self) -> &[&str] {
128        COMPATIBLE
129    }
130
131    /// Performs the init operation.
132    fn init(&mut self, base: usize) -> Result<(), BusError> {
133        self.regs.init(base, MAX_DRBS * CDMM_DRB_SIZE);
134        self.discover_devices();
135        self.power_state = PowerState::On;
136        Ok(())
137    }
138
139    /// Performs the shutdown operation.
140    fn shutdown(&mut self) -> Result<(), BusError> {
141        self.power_state = PowerState::Off;
142        Ok(())
143    }
144
145    /// Reads reg.
146    fn read_reg(&self, offset: usize) -> Result<u32, BusError> {
147        if !self.regs.is_valid() {
148            return Err(BusError::InitFailed);
149        }
150        self.regs.check_user_offset(offset)?;
151        Ok(self.regs.read32(offset))
152    }
153
154    /// Writes reg.
155    fn write_reg(&mut self, offset: usize, value: u32) -> Result<(), BusError> {
156        if !self.regs.is_valid() {
157            return Err(BusError::InitFailed);
158        }
159        self.regs.check_user_offset(offset)?;
160        self.regs.write32(offset, value);
161        Ok(())
162    }
163
164    /// Performs the children operation.
165    fn children(&self) -> Vec<BusChild> {
166        self.devices
167            .iter()
168            .enumerate()
169            .map(|(i, d)| BusChild {
170                name: alloc::format!("cdmm-dev-{:02x}", d.dev_type),
171                base_addr: d.drb_offset as u64,
172                size: (d.dev_size.max(1) as u64) * (CDMM_DRB_SIZE as u64),
173            })
174            .collect()
175    }
176}