Skip to main content

strat9_bus_drivers/
vexpress_config.rs

1use crate::{BusChild, BusDriver, BusError, PowerState, mmio::MmioRegion};
2use alloc::{string::String, vec::Vec};
3use core::sync::atomic::{AtomicBool, Ordering};
4
5const SYS_MISC: usize = 0x00;
6const SYS_MISC_MASTERSITE: u32 = 1 << 14;
7
8const SYS_PROCID0: usize = 0x24;
9const SYS_PROCID1: usize = 0x28;
10const SYS_HBI_MASK: u32 = 0xFFF;
11
12const SYS_CFGDATA: usize = 0x40;
13const SYS_CFGCTRL: usize = 0x44;
14const SYS_CFGCTRL_START: u32 = 1 << 31;
15const SYS_CFGCTRL_WRITE: u32 = 1 << 30;
16const SYS_CFGSTAT: usize = 0x48;
17const SYS_CFGSTAT_ERR: u32 = 1 << 1;
18const SYS_CFGSTAT_COMPLETE: u32 = 1 << 0;
19
20const SITE_MB: u32 = 0;
21const SITE_DB1: u32 = 1;
22const SITE_DB2: u32 = 2;
23const SITE_MASTER: u32 = 0xF;
24
25const MAX_POLL_TRIES: u32 = 100;
26
27// === DCC (Device Configuration Clock) ======================================
28
29/// Offset of the DCC control register within the DCC register block.
30const DCC_CTRL_REG: usize = 0x0;
31/// DCC control: enable bit.
32const DCC_CTRL_ENABLE: u32 = 1 << 0;
33/// DCC control: divider field mask (clock = reference / divider).
34const DCC_CTRL_DIV_MASK: u32 = 0xFF << 8;
35
36/// Target config-bus clock (MHz), per the VExpress syscfg specification.
37pub const DCC_TARGET_FREQ_MHZ: u32 = 50;
38/// Motherboard reference clock feeding the DCC (MHz).
39pub const DCC_REF_FREQ_MHZ: u32 = 100;
40
41fn dcc_ctrl_divider(div: u32) -> u32 {
42    ((div.max(1)) << 8) & DCC_CTRL_DIV_MASK
43}
44
45const COMPATIBLE: &[&str] = &["vexpress-syscfg"];
46
47/// Performs the cfg ctrl dcc operation.
48fn cfg_ctrl_dcc(n: u32) -> u32 {
49    (n & 0xF) << 26
50}
51/// Performs the cfg ctrl func operation.
52fn cfg_ctrl_func(n: u32) -> u32 {
53    (n & 0x3F) << 20
54}
55/// Performs the cfg ctrl site operation.
56fn cfg_ctrl_site(n: u32) -> u32 {
57    (n & 0x3) << 16
58}
59/// Performs the cfg ctrl position operation.
60fn cfg_ctrl_position(n: u32) -> u32 {
61    (n & 0xF) << 12
62}
63/// Performs the cfg ctrl device operation.
64fn cfg_ctrl_device(n: u32) -> u32 {
65    n & 0xFFF
66}
67
68pub struct VexpressConfig {
69    regs: MmioRegion,
70    /// DCC clock-generator register block, mapped separately from the
71    /// syscfg registers (platform-dependent base).
72    dcc_regs: MmioRegion,
73    /// True once the DCC has been programmed; guarded atomically because
74    /// transactions are issued through `&self`.
75    dcc_initialized: AtomicBool,
76    master_site: u32,
77    power_state: PowerState,
78}
79
80impl VexpressConfig {
81    /// Creates a new instance.
82    pub fn new() -> Self {
83        Self {
84            regs: MmioRegion::new(),
85            dcc_regs: MmioRegion::new(),
86            dcc_initialized: AtomicBool::new(false),
87            master_site: SITE_MASTER,
88            power_state: PowerState::Off,
89        }
90    }
91
92    /// Maps the DCC register block. Must be called before [`Self::init`]
93    /// for config-bus transactions to be allowed (the DCC must run before
94    /// any transaction can succeed, per the VExpress syscfg spec).
95    pub fn init_dcc_regs(&mut self, base: usize) {
96        self.dcc_regs.init(base, 0x10);
97    }
98
99    /// Programs the DCC to run the config bus at
100    /// [`DCC_TARGET_FREQ_MHZ`] from [`DCC_REF_FREQ_MHZ`], exactly once.
101    fn ensure_dcc_ready(&self) -> Result<(), BusError> {
102        if self.dcc_initialized.load(Ordering::Acquire) {
103            return Ok(());
104        }
105        if !self.dcc_regs.is_valid() {
106            return Err(BusError::InitFailed);
107        }
108        let div = (DCC_REF_FREQ_MHZ / DCC_TARGET_FREQ_MHZ).max(1);
109        self.dcc_regs
110            .write32(DCC_CTRL_REG, DCC_CTRL_ENABLE | dcc_ctrl_divider(div));
111        // Read-back verification, like the rest of our MMIO drivers.
112        let rb = self.dcc_regs.read32(DCC_CTRL_REG);
113        if rb != (DCC_CTRL_ENABLE | dcc_ctrl_divider(div)) {
114            return Err(BusError::BusFault);
115        }
116        self.dcc_initialized.store(true, Ordering::Release);
117        Ok(())
118    }
119
120    /// Performs the detect master site operation.
121    fn detect_master_site(&mut self) {
122        let misc = self.regs.read32(SYS_MISC);
123        self.master_site = if misc & SYS_MISC_MASTERSITE != 0 {
124            SITE_DB2
125        } else {
126            SITE_DB1
127        };
128    }
129
130    /// Reads procid.
131    pub fn read_procid(&self, site: u32) -> u32 {
132        let offset = if site == SITE_DB1 {
133            SYS_PROCID0
134        } else {
135            SYS_PROCID1
136        };
137        self.regs.read32(offset)
138    }
139
140    /// Performs the hbi operation.
141    pub fn hbi(&self) -> u32 {
142        let id = self.read_procid(self.master_site);
143        id & SYS_HBI_MASK
144    }
145
146    /// Performs the config read operation.
147    pub fn config_read(
148        &self,
149        site: u32,
150        position: u32,
151        dcc: u32,
152        function: u32,
153        device: u32,
154    ) -> Result<u32, BusError> {
155        self.ensure_dcc_ready()?;
156
157        let command = self.regs.read32(SYS_CFGCTRL);
158        if command & SYS_CFGCTRL_START != 0 {
159            return Err(BusError::Timeout);
160        }
161
162        let real_site = if site == SITE_MASTER {
163            self.master_site
164        } else {
165            site
166        };
167
168        let cmd = SYS_CFGCTRL_START
169            | cfg_ctrl_dcc(dcc)
170            | cfg_ctrl_site(real_site)
171            | cfg_ctrl_position(position)
172            | cfg_ctrl_func(function)
173            | cfg_ctrl_device(device);
174
175        self.regs.write32(SYS_CFGDATA, 0xDEAD_BEEF);
176        self.regs.write32(SYS_CFGSTAT, 0);
177        self.regs.write32(SYS_CFGCTRL, cmd);
178        crate::mmio::memory_barrier();
179
180        for _ in 0..MAX_POLL_TRIES {
181            let status = self.regs.read32(SYS_CFGSTAT);
182            if status & SYS_CFGSTAT_ERR != 0 {
183                return Err(BusError::IoError);
184            }
185            if status & SYS_CFGSTAT_COMPLETE != 0 {
186                return Ok(self.regs.read32(SYS_CFGDATA));
187            }
188        }
189
190        Err(BusError::Timeout)
191    }
192
193    /// Performs the config write operation.
194    pub fn config_write(
195        &self,
196        site: u32,
197        position: u32,
198        dcc: u32,
199        function: u32,
200        device: u32,
201        data: u32,
202    ) -> Result<(), BusError> {
203        self.ensure_dcc_ready()?;
204
205        let command = self.regs.read32(SYS_CFGCTRL);
206        if command & SYS_CFGCTRL_START != 0 {
207            return Err(BusError::Timeout);
208        }
209
210        let real_site = if site == SITE_MASTER {
211            self.master_site
212        } else {
213            site
214        };
215
216        let cmd = SYS_CFGCTRL_START
217            | SYS_CFGCTRL_WRITE
218            | cfg_ctrl_dcc(dcc)
219            | cfg_ctrl_site(real_site)
220            | cfg_ctrl_position(position)
221            | cfg_ctrl_func(function)
222            | cfg_ctrl_device(device);
223
224        self.regs.write32(SYS_CFGDATA, data);
225        self.regs.write32(SYS_CFGSTAT, 0);
226        self.regs.write32(SYS_CFGCTRL, cmd);
227        crate::mmio::memory_barrier();
228
229        for _ in 0..MAX_POLL_TRIES {
230            let status = self.regs.read32(SYS_CFGSTAT);
231            if status & SYS_CFGSTAT_ERR != 0 {
232                return Err(BusError::IoError);
233            }
234            if status & SYS_CFGSTAT_COMPLETE != 0 {
235                return Ok(());
236            }
237        }
238
239        Err(BusError::Timeout)
240    }
241}
242
243impl BusDriver for VexpressConfig {
244    /// Performs the name operation.
245    fn name(&self) -> &str {
246        "vexpress-config"
247    }
248
249    /// Performs the compatible operation.
250    fn compatible(&self) -> &[&str] {
251        COMPATIBLE
252    }
253
254    /// Performs the init operation.
255    fn init(&mut self, base: usize) -> Result<(), BusError> {
256        self.regs.init(base, 0x100);
257        self.detect_master_site();
258        self.power_state = PowerState::On;
259        Ok(())
260    }
261
262    /// Performs the shutdown operation.
263    fn shutdown(&mut self) -> Result<(), BusError> {
264        self.power_state = PowerState::Off;
265        Ok(())
266    }
267
268    /// Reads reg.
269    fn read_reg(&self, offset: usize) -> Result<u32, BusError> {
270        if !self.regs.is_valid() {
271            return Err(BusError::InitFailed);
272        }
273        self.regs.check_user_offset(offset)?;
274        Ok(self.regs.read32(offset))
275    }
276
277    /// Writes reg.
278    fn write_reg(&mut self, offset: usize, value: u32) -> Result<(), BusError> {
279        if !self.regs.is_valid() {
280            return Err(BusError::InitFailed);
281        }
282        self.regs.check_user_offset(offset)?;
283        self.regs.write32(offset, value);
284        Ok(())
285    }
286}