Skip to main content

strat9_bus_drivers/
brcmstb_gisb.rs

1use crate::{BusChild, BusDriver, BusError, PowerState, mmio::MmioRegion};
2use alloc::{string::String, vec::Vec};
3use core::sync::atomic::{AtomicU64, Ordering};
4
5const ARB_ERR_CAP_CLEAR: u32 = 0x0001;
6const ARB_ERR_CAP_STATUS_TIMEOUT: u32 = 1 << 12;
7const ARB_ERR_CAP_STATUS_TEA: u32 = 1 << 11;
8const ARB_ERR_CAP_STATUS_WRITE: u32 = 1 << 1;
9const ARB_ERR_CAP_STATUS_VALID: u32 = 1 << 0;
10
11const ARB_BP_CAP_CLEAR: u32 = 1 << 0;
12const ARB_BP_CAP_STATUS_WRITE: u32 = 1 << 1;
13const ARB_BP_CAP_STATUS_VALID: u32 = 1 << 0;
14
15const COMPATIBLE: &[&str] = &[
16    "brcm,bcm7038-gisb-arb",
17    "brcm,bcm7278-gisb-arb",
18    "brcm,bcm7400-gisb-arb",
19    "brcm,bcm74165-gisb-arb",
20    "brcm,bcm7435-gisb-arb",
21    "brcm,bcm7445-gisb-arb",
22];
23
24#[derive(Clone, Copy)]
25pub struct GisbOffsets {
26    pub arb_timer: usize,
27    pub arb_bp_cap_clr: usize,
28    pub arb_bp_cap_addr: usize,
29    pub arb_bp_cap_status: usize,
30    pub arb_bp_cap_master: Option<usize>,
31    pub arb_err_cap_clr: usize,
32    pub arb_err_cap_addr: usize,
33    pub arb_err_cap_status: usize,
34    pub arb_err_cap_master: Option<usize>,
35    pub arb_err_cap_hi_addr: Option<usize>,
36}
37
38pub const BCM7038_OFFSETS: GisbOffsets = GisbOffsets {
39    arb_timer: 0x00C,
40    arb_bp_cap_clr: 0x014,
41    arb_bp_cap_addr: 0x0B8,
42    arb_bp_cap_status: 0x0C0,
43    arb_bp_cap_master: None,
44    arb_err_cap_clr: 0x0C4,
45    arb_err_cap_addr: 0x0C8,
46    arb_err_cap_status: 0x0D0,
47    arb_err_cap_master: None,
48    arb_err_cap_hi_addr: None,
49};
50
51pub const BCM7445_OFFSETS: GisbOffsets = GisbOffsets {
52    arb_timer: 0x008,
53    arb_bp_cap_clr: 0x010,
54    arb_bp_cap_addr: 0x1D8,
55    arb_bp_cap_status: 0x1E0,
56    arb_bp_cap_master: Some(0x1E4),
57    arb_err_cap_clr: 0x7E4,
58    arb_err_cap_addr: 0x7EC,
59    arb_err_cap_status: 0x7F4,
60    arb_err_cap_master: Some(0x7F8),
61    arb_err_cap_hi_addr: Some(0x7E8),
62};
63
64pub const BCM7278_OFFSETS: GisbOffsets = GisbOffsets {
65    arb_timer: 0x008,
66    arb_bp_cap_clr: 0x01C,
67    arb_bp_cap_addr: 0x220,
68    arb_bp_cap_status: 0x230,
69    arb_bp_cap_master: Some(0x234),
70    arb_err_cap_clr: 0x7F8,
71    arb_err_cap_addr: 0x7E0,
72    arb_err_cap_status: 0x7F0,
73    arb_err_cap_master: Some(0x7F4),
74    arb_err_cap_hi_addr: None,
75};
76
77pub struct GisbErrorInfo {
78    pub address: u64,
79    pub master: Option<u32>,
80    pub is_write: bool,
81    pub is_timeout: bool,
82    pub is_tea: bool,
83}
84
85pub struct BrcmstbGisb {
86    regs: MmioRegion,
87    offsets: GisbOffsets,
88    error_count: AtomicU64,
89    big_endian: bool,
90    saved_timeout: u32,
91    master_names: Vec<String>,
92    power_state: PowerState,
93}
94
95impl BrcmstbGisb {
96    /// Creates a new instance.
97    pub fn new(offsets: GisbOffsets) -> Self {
98        Self {
99            regs: MmioRegion::new(),
100            offsets,
101            error_count: AtomicU64::new(0),
102            big_endian: false,
103            saved_timeout: 0,
104            master_names: Vec::new(),
105            power_state: PowerState::Off,
106        }
107    }
108
109    /// Sets big endian.
110    pub fn set_big_endian(&mut self, big_endian: bool) {
111        self.big_endian = big_endian;
112    }
113
114    /// Replaces the bus-master name table.
115    ///
116    /// Entry `i` names the hardware master whose ID reads back as `i`
117    /// from `arb_err_cap_master`/`arb_bp_cap_master`. Tables are
118    /// generation-specific; they must be supplied by platform code
119    /// (DeviceTree/board support), mirroring Linux's
120    /// `gisb_master_names_*[]` per-SoC tables.
121    pub fn set_master_names<I: IntoIterator<Item = String>>(&mut self, names: I) {
122        self.master_names.clear();
123        self.master_names.extend(names);
124    }
125
126    /// Appends one entry to the master-name table (see [`Self::set_master_names`]).
127    /// Entries are appended **in order**, so this positions the name at the
128    /// next free master-ID slot.
129    pub fn add_master_name(&mut self, name: String) {
130        self.master_names.push(name);
131    }
132
133    /// Resolves a captured master ID to its human-readable name.
134    ///
135    /// - `None` (generation without a master register) => `"none"`;
136    /// - ID present in the configured table => the platform-provided name;
137    /// - otherwise => `"master<N>"`, still more useful than an empty string.
138    pub fn master_name(&self, master: Option<u32>) -> alloc::string::String {
139        match master {
140            None => alloc::format!("none"),
141            Some(id) => match self.master_names.get(id as usize) {
142                Some(name) => name.clone(),
143                None => alloc::format!("master{}", id),
144            },
145        }
146    }
147
148    /// Reads gisb.
149    fn read_gisb(&self, offset: usize) -> u32 {
150        let val = self.regs.read32(offset);
151        if self.big_endian {
152            val.swap_bytes()
153        } else {
154            val
155        }
156    }
157
158    /// Writes gisb.
159    fn write_gisb(&self, offset: usize, val: u32) {
160        let val = if self.big_endian {
161            val.swap_bytes()
162        } else {
163            val
164        };
165        self.regs.write32(offset, val);
166    }
167
168    /// Returns timeout.
169    pub fn get_timeout(&self) -> u32 {
170        self.read_gisb(self.offsets.arb_timer)
171    }
172
173    /// Sets timeout.
174    pub fn set_timeout(&self, val: u32) {
175        self.write_gisb(self.offsets.arb_timer, val);
176    }
177
178    /// Formats a captured GISB error into a one-line human-readable report,
179    /// e.g. `gisb timeout write addr=0x00000000deadbeef master=ethsw`.
180    /// This is the string platform IRQ handlers should emit in their logs.
181    pub fn format_error(&self, info: &GisbErrorInfo) -> alloc::string::String {
182        let kind = if info.is_timeout {
183            "timeout"
184        } else if info.is_tea {
185            "tea"
186        } else {
187            "error"
188        };
189        let rw = if info.is_write { "write" } else { "read" };
190        alloc::format!(
191            "gisb {} {} addr=0x{:016x} master={}",
192            kind,
193            rw,
194            info.address,
195            self.master_name(info.master)
196        )
197    }
198
199    /// Handles timeout irq.
200    pub fn handle_timeout_irq(&self) -> Option<GisbErrorInfo> {
201        let status = self.read_gisb(self.offsets.arb_err_cap_status);
202        if status & ARB_ERR_CAP_STATUS_VALID == 0 {
203            return None;
204        }
205
206        let addr_lo = self.read_gisb(self.offsets.arb_err_cap_addr);
207        let addr_hi = self
208            .offsets
209            .arb_err_cap_hi_addr
210            .map(|off| self.read_gisb(off))
211            .unwrap_or(0);
212        let master = self
213            .offsets
214            .arb_err_cap_master
215            .map(|off| self.read_gisb(off));
216
217        let info = GisbErrorInfo {
218            address: ((addr_hi as u64) << 32) | (addr_lo as u64),
219            master,
220            is_write: (status & ARB_ERR_CAP_STATUS_WRITE) != 0,
221            is_timeout: (status & ARB_ERR_CAP_STATUS_TIMEOUT) != 0,
222            is_tea: (status & ARB_ERR_CAP_STATUS_TEA) != 0,
223        };
224
225        self.write_gisb(self.offsets.arb_err_cap_clr, ARB_ERR_CAP_CLEAR);
226
227        Some(info)
228    }
229
230    /// Handles bp irq.
231    pub fn handle_bp_irq(&self) -> Option<GisbErrorInfo> {
232        let status = self.read_gisb(self.offsets.arb_bp_cap_status);
233        if status & ARB_BP_CAP_STATUS_VALID == 0 {
234            return None;
235        }
236
237        let addr = self.read_gisb(self.offsets.arb_bp_cap_addr);
238        let master = self
239            .offsets
240            .arb_bp_cap_master
241            .map(|off| self.read_gisb(off));
242
243        let info = GisbErrorInfo {
244            address: addr as u64,
245            master,
246            is_write: (status & ARB_BP_CAP_STATUS_WRITE) != 0,
247            is_timeout: false,
248            is_tea: false,
249        };
250
251        self.write_gisb(self.offsets.arb_bp_cap_clr, ARB_BP_CAP_CLEAR);
252
253        Some(info)
254    }
255}
256
257impl BusDriver for BrcmstbGisb {
258    /// Performs the name operation.
259    fn name(&self) -> &str {
260        "brcmstb-gisb"
261    }
262
263    /// Performs the compatible operation.
264    fn compatible(&self) -> &[&str] {
265        COMPATIBLE
266    }
267
268    /// Performs the init operation.
269    fn init(&mut self, base: usize) -> Result<(), BusError> {
270        self.regs.init(base, 0x800);
271        self.power_state = PowerState::On;
272        Ok(())
273    }
274
275    /// Performs the shutdown operation.
276    fn shutdown(&mut self) -> Result<(), BusError> {
277        self.power_state = PowerState::Off;
278        Ok(())
279    }
280
281    /// Performs the suspend operation.
282    fn suspend(&mut self) -> Result<(), BusError> {
283        self.saved_timeout = self.get_timeout();
284        self.power_state = PowerState::Suspended;
285        Ok(())
286    }
287
288    /// Performs the resume operation.
289    fn resume(&mut self) -> Result<(), BusError> {
290        self.set_timeout(self.saved_timeout);
291        self.power_state = PowerState::On;
292        Ok(())
293    }
294
295    /// Reads reg.
296    fn read_reg(&self, offset: usize) -> Result<u32, BusError> {
297        if !self.regs.is_valid() {
298            return Err(BusError::InitFailed);
299        }
300        self.regs.check_user_offset(offset)?;
301        Ok(self.read_gisb(offset))
302    }
303
304    /// Writes reg.
305    fn write_reg(&mut self, offset: usize, value: u32) -> Result<(), BusError> {
306        if !self.regs.is_valid() {
307            return Err(BusError::InitFailed);
308        }
309        self.regs.check_user_offset(offset)?;
310        self.write_gisb(offset, value);
311        Ok(())
312    }
313
314    /// Performs the error count operation.
315    fn error_count(&self) -> u64 {
316        self.error_count.load(Ordering::Relaxed)
317    }
318
319    /// Handles irq.
320    fn handle_irq(&mut self) -> bool {
321        let timeout = self.handle_timeout_irq();
322        let bp = self.handle_bp_irq();
323        if timeout.is_some() || bp.is_some() {
324            self.error_count.fetch_add(1, Ordering::Relaxed);
325            true
326        } else {
327            false
328        }
329    }
330}