Skip to main content

strat9_bus_drivers/
arm_cci.rs

1use crate::{BusChild, BusDriver, BusError, PowerState, mmio::MmioRegion};
2use alloc::{string::String, vec::Vec};
3
4const CCI_PORT_CTRL: usize = 0x00;
5const CCI_CTRL_STATUS: usize = 0x0C;
6
7const CCI_ENABLE_SNOOP_REQ: u32 = 0x1;
8const CCI_ENABLE_DVM_REQ: u32 = 0x2;
9const CCI_ENABLE_REQ: u32 = CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ;
10
11const PORT_VALID_SHIFT: u32 = 31;
12const PORT_VALID: u32 = 1 << PORT_VALID_SHIFT;
13
14const MAX_PORTS: usize = 5;
15const MAX_POLL: u32 = 10000;
16
17const COMPATIBLE: &[&str] = &["arm,cci-400", "arm,cci-500", "arm,cci-550"];
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum CciPortType {
21    Ace,
22    AceLite,
23}
24
25#[derive(Clone)]
26pub struct CciPort {
27    pub port_type: CciPortType,
28    pub base_offset: usize,
29    pub enabled: bool,
30}
31
32pub struct ArmCci {
33    regs: MmioRegion,
34    ports: [Option<CciPort>; MAX_PORTS],
35    nb_ace: usize,
36    nb_ace_lite: usize,
37    power_state: PowerState,
38}
39
40impl ArmCci {
41    /// Creates a new instance.
42    pub fn new() -> Self {
43        Self {
44            regs: MmioRegion::new(),
45            ports: [const { None }; MAX_PORTS],
46            nb_ace: 0,
47            nb_ace_lite: 0,
48            power_state: PowerState::Off,
49        }
50    }
51
52    /// Sets port counts.
53    pub fn set_port_counts(&mut self, ace: usize, ace_lite: usize) {
54        self.nb_ace = ace;
55        self.nb_ace_lite = ace_lite;
56    }
57
58    /// Performs the add port operation.
59    pub fn add_port(&mut self, index: usize, port_type: CciPortType, base_offset: usize) {
60        if index < MAX_PORTS {
61            self.ports[index] = Some(CciPort {
62                port_type,
63                base_offset,
64                enabled: false,
65            });
66        }
67    }
68
69    /// Enables port.
70    pub fn enable_port(&mut self, index: usize) -> Result<(), BusError> {
71        if index >= MAX_PORTS {
72            return Err(BusError::InvalidArgument);
73        }
74        let base_offset = self.ports[index]
75            .as_ref()
76            .ok_or(BusError::DeviceNotFound)?
77            .base_offset;
78        self.regs
79            .write32(base_offset + CCI_PORT_CTRL, CCI_ENABLE_REQ);
80        self.wait_for_status_clear()?;
81        let port = self.ports[index].as_mut().ok_or(BusError::DeviceNotFound)?;
82        port.enabled = true;
83        Ok(())
84    }
85
86    /// Disables port.
87    pub fn disable_port(&mut self, index: usize) -> Result<(), BusError> {
88        if index >= MAX_PORTS {
89            return Err(BusError::InvalidArgument);
90        }
91        let base_offset = self.ports[index]
92            .as_ref()
93            .ok_or(BusError::DeviceNotFound)?
94            .base_offset;
95        self.regs.write32(base_offset + CCI_PORT_CTRL, 0);
96        self.wait_for_status_clear()?;
97        let port = self.ports[index].as_mut().ok_or(BusError::DeviceNotFound)?;
98        port.enabled = false;
99        Ok(())
100    }
101
102    /// Performs the wait for status clear operation.
103    fn wait_for_status_clear(&self) -> Result<(), BusError> {
104        for _ in 0..MAX_POLL {
105            let status = self.regs.read32(CCI_CTRL_STATUS);
106            if status & 1 == 0 {
107                return Ok(());
108            }
109        }
110        Err(BusError::Timeout)
111    }
112}
113
114impl BusDriver for ArmCci {
115    /// Performs the name operation.
116    fn name(&self) -> &str {
117        "arm-cci"
118    }
119
120    /// Performs the compatible operation.
121    fn compatible(&self) -> &[&str] {
122        COMPATIBLE
123    }
124
125    /// Performs the init operation.
126    fn init(&mut self, base: usize) -> Result<(), BusError> {
127        self.regs.init(base, 0x10000);
128        self.power_state = PowerState::On;
129        Ok(())
130    }
131
132    /// Performs the shutdown operation.
133    fn shutdown(&mut self) -> Result<(), BusError> {
134        for i in 0..MAX_PORTS {
135            if self.ports[i].as_ref().map_or(false, |p| p.enabled) {
136                let _ = self.disable_port(i);
137            }
138        }
139        self.power_state = PowerState::Off;
140        Ok(())
141    }
142
143    /// Reads reg.
144    fn read_reg(&self, offset: usize) -> Result<u32, BusError> {
145        if !self.regs.is_valid() {
146            return Err(BusError::InitFailed);
147        }
148        Ok(self.regs.read32(offset))
149    }
150
151    /// Writes reg.
152    fn write_reg(&mut self, offset: usize, value: u32) -> Result<(), BusError> {
153        if !self.regs.is_valid() {
154            return Err(BusError::InitFailed);
155        }
156        self.regs.write32(offset, value);
157        Ok(())
158    }
159
160    /// Performs the children operation.
161    fn children(&self) -> Vec<BusChild> {
162        let mut children = Vec::new();
163        for (i, port) in self.ports.iter().enumerate() {
164            if let Some(p) = port {
165                let type_name = match p.port_type {
166                    CciPortType::Ace => "ace",
167                    CciPortType::AceLite => "ace-lite",
168                };
169                children.push(BusChild {
170                    name: String::from(type_name),
171                    base_addr: p.base_offset as u64,
172                    size: 0x1000,
173                });
174            }
175        }
176        children
177    }
178}