Skip to main content

strat9_bus_drivers/
sun50i_de2.rs

1use crate::{BusChild, BusDriver, BusError, PowerState, mmio::MmioRegion};
2use alloc::{string::String, vec::Vec};
3
4const COMPATIBLE: &[&str] = &["allwinner,sun50i-a64-de2"];
5
6/// Configuration of the Allwinner SRAM controller (SRAMC) region that the
7/// Display Engine 2 must own to operate.
8///
9/// The exact register layout differs across sunxi SoC generations; platform
10/// code (board support / DeviceTree) supplies it explicitly : mirroring the
11/// `sunxi_sram_claim()` DT-driven flow in Linux (`drivers/soc/sunxi/sunxi_sram.c`).
12#[derive(Debug, Clone, Copy)]
13pub struct SramConfig {
14    /// Physical base address of the SRAM controller register block
15    /// (A64: `0x01C0_0000`).
16    pub ctrl_base: usize,
17    /// Offset of the control register owning the DE2 SRAM region.
18    pub ctrl_offset: usize,
19    /// Mask of the ownership field inside the control register.
20    pub owner_mask: u32,
21    /// Value within the field selecting the display engine as owner.
22    pub de2_owner: u32,
23}
24
25impl SramConfig {
26    /// Applies `de2_owner` into the masked field of `current`.
27    fn apply_field(current: u32, cfg: &Self) -> u32 {
28        let shift = cfg.owner_mask.trailing_zeros();
29        (current & !cfg.owner_mask) | ((cfg.de2_owner << shift) & cfg.owner_mask)
30    }
31}
32
33pub struct Sun50iDe2 {
34    regs: MmioRegion,
35    power_state: PowerState,
36    sram_claimed: bool,
37    sram_config: Option<SramConfig>,
38    children: Vec<BusChild>,
39}
40
41impl Sun50iDe2 {
42    /// Creates a new instance.
43    pub fn new() -> Self {
44        Self {
45            regs: MmioRegion::new(),
46            power_state: PowerState::Off,
47            sram_claimed: false,
48            sram_config: None,
49            children: Vec::new(),
50        }
51    }
52
53    /// Supplies the SRAM-controller configuration required by
54    /// [`Self::claim_sram`] before [`BusDriver::init`] can succeed.
55    pub fn set_sram_config(&mut self, cfg: SramConfig) {
56        self.sram_config = Some(cfg);
57    }
58
59    /// Claims DE2 ownership of its SRAM region: read-modify-write of the
60    /// SRAM controller control register to route the region away from the
61    /// CPU/debug default owner, followed by a read-back verification.
62    ///
63    /// Fails with [`BusError::InitFailed`] when no configuration was
64    /// supplied, and with [`BusError::BusFault`] when the read-back does
65    /// not confirm the new ownership (controller absent or wedged).
66    pub fn claim_sram(&mut self) -> Result<(), BusError> {
67        let cfg = match &self.sram_config {
68            Some(cfg) => *cfg,
69            None => return Err(BusError::InitFailed),
70        };
71
72        // SAFETY: platform code guarantees `ctrl_base + ctrl_offset` maps
73        // the SRAM controller control register when a config is supplied.
74        let addr = (cfg.ctrl_base + cfg.ctrl_offset) as *mut u32;
75        let current = unsafe { core::ptr::read_volatile(addr) };
76        let updated = SramConfig::apply_field(current, &cfg);
77        unsafe { core::ptr::write_volatile(addr, updated) };
78
79        // Verify the controller accepted the ownership switch.
80        let readback = unsafe { core::ptr::read_volatile(addr) };
81        if readback != updated {
82            return Err(BusError::BusFault);
83        }
84
85        self.sram_claimed = true;
86        Ok(())
87    }
88
89    /// Releases DE2 SRAM ownership, restoring the default (CPU) owner.
90    /// A missing configuration or failed write leaves the flag cleared so
91    /// a later `shutdown`/`claim` cycle starts from a known state.
92    pub fn release_sram(&mut self) {
93        if let Some(cfg) = self.sram_config {
94            // SAFETY: same mapping as in `claim_sram`.
95            let addr = (cfg.ctrl_base + cfg.ctrl_offset) as *mut u32;
96            let current = unsafe { core::ptr::read_volatile(addr) };
97            let restored = current & !cfg.owner_mask;
98            unsafe { core::ptr::write_volatile(addr, restored) };
99        }
100        self.sram_claimed = false;
101    }
102
103    /// Returns true while this instance owns the DE2 SRAM region.
104    pub fn sram_claimed(&self) -> bool {
105        self.sram_claimed
106    }
107
108    /// Performs the add child operation.
109    pub fn add_child(&mut self, child: BusChild) {
110        self.children.push(child);
111    }
112}
113
114impl BusDriver for Sun50iDe2 {
115    /// Performs the name operation.
116    fn name(&self) -> &str {
117        "sun50i-de2"
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, 0x1000);
128        self.claim_sram()?;
129        self.power_state = PowerState::On;
130        Ok(())
131    }
132
133    /// Performs the shutdown operation.
134    fn shutdown(&mut self) -> Result<(), BusError> {
135        self.release_sram();
136        self.power_state = PowerState::Off;
137        Ok(())
138    }
139
140    /// Reads reg.
141    fn read_reg(&self, offset: usize) -> Result<u32, BusError> {
142        if !self.regs.is_valid() {
143            return Err(BusError::InitFailed);
144        }
145        self.regs.check_user_offset(offset)?;
146        Ok(self.regs.read32(offset))
147    }
148
149    /// Writes reg.
150    fn write_reg(&mut self, offset: usize, value: u32) -> Result<(), BusError> {
151        if !self.regs.is_valid() {
152            return Err(BusError::InitFailed);
153        }
154        self.regs.check_user_offset(offset)?;
155        self.regs.write32(offset, value);
156        Ok(())
157    }
158
159    /// Performs the children operation.
160    fn children(&self) -> Vec<BusChild> {
161        self.children.clone()
162    }
163}