Skip to main content

strat9_bus_drivers/
qcom_ssc_block_bus.rs

1use crate::{BusChild, BusDriver, BusError, PowerState, mmio::MmioRegion};
2use alloc::{string::String, vec::Vec};
3
4const AXI_HALTREQ_REG: usize = 0x0;
5const AXI_HALTACK_REG: usize = 0x4;
6const AXI_IDLE_REG: usize = 0x8;
7
8const SSCAON_CONFIG0_CLAMP_EN_OVRD: u32 = 1 << 4;
9const SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL: u32 = 1 << 5;
10const SSCAON_CONFIG1_CFG: u32 = 1 << 31;
11
12const MAX_HALT_WAIT: u32 = 10000;
13
14const COMPATIBLE: &[&str] = &["qcom,ssc-block-bus"];
15
16pub struct QcomSscBlockBus {
17    halt_regs: MmioRegion,
18    config0_regs: MmioRegion,
19    config1_regs: MmioRegion,
20    power_state: PowerState,
21    children: Vec<BusChild>,
22}
23
24impl QcomSscBlockBus {
25    /// Creates a new instance.
26    pub fn new() -> Self {
27        Self {
28            halt_regs: MmioRegion::new(),
29            config0_regs: MmioRegion::new(),
30            config1_regs: MmioRegion::new(),
31            power_state: PowerState::Off,
32            children: Vec::new(),
33        }
34    }
35
36    /// Initializes halt regs.
37    pub fn init_halt_regs(&mut self, base: usize) {
38        self.halt_regs.init(base, 0x10);
39    }
40
41    /// Initializes config regs.
42    pub fn init_config_regs(&mut self, config0_base: usize, config1_base: usize) {
43        self.config0_regs.init(config0_base, 0x10);
44        self.config1_regs.init(config1_base, 0x10);
45    }
46
47    /// Ensures every MMIO region (halt, config0, config1) has been mapped
48    /// by platform code before any register access.
49    ///
50    /// Accessing an unmapped [`MmioRegion`] would trip its internal
51    /// assertions (kernel panic); failing early with
52    /// [`BusError::InitFailed`] turns a hard crash into a clean
53    /// driver-registration failure instead.
54    fn ensure_mapped(&self) -> Result<(), BusError> {
55        if self.halt_regs.is_valid() && self.config0_regs.is_valid() && self.config1_regs.is_valid()
56        {
57            Ok(())
58        } else {
59            Err(BusError::InitFailed)
60        }
61    }
62
63    /// Performs the bus init operation.
64    fn bus_init(&self) -> Result<(), BusError> {
65        self.ensure_mapped()?;
66
67        self.config0_regs
68            .clear_bits32(0, SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL);
69        self.config0_regs
70            .set_bits32(0, SSCAON_CONFIG0_CLAMP_EN_OVRD);
71        self.config1_regs.clear_bits32(0, SSCAON_CONFIG1_CFG);
72
73        self.halt_regs.write32(AXI_HALTREQ_REG, 0);
74
75        for _ in 0..MAX_HALT_WAIT {
76            let idle = self.halt_regs.read32(AXI_IDLE_REG);
77            if idle != 0 {
78                return Ok(());
79            }
80        }
81
82        // The AXI bus never reached idle: report it instead of silently
83        // claiming success.
84        Err(BusError::Timeout)
85    }
86
87    /// Performs the bus deinit operation.
88    ///
89    /// The safety clamps are always applied, even when the halt handshake
90    /// times out; the timeout is reported through the returned error.
91    fn bus_deinit(&self) -> Result<(), BusError> {
92        self.ensure_mapped()?;
93
94        self.halt_regs.write32(AXI_HALTREQ_REG, 1);
95
96        let mut halted = false;
97        for _ in 0..MAX_HALT_WAIT {
98            let ack = self.halt_regs.read32(AXI_HALTACK_REG);
99            if ack != 0 {
100                halted = true;
101                break;
102            }
103        }
104
105        self.config0_regs
106            .set_bits32(0, SSCAON_CONFIG0_CLAMP_EN_OVRD_VAL);
107        self.config1_regs.set_bits32(0, SSCAON_CONFIG1_CFG);
108
109        if halted {
110            Ok(())
111        } else {
112            Err(BusError::Timeout)
113        }
114    }
115
116    /// Performs the add child operation.
117    pub fn add_child(&mut self, child: BusChild) {
118        self.children.push(child);
119    }
120}
121
122impl BusDriver for QcomSscBlockBus {
123    /// Performs the name operation.
124    fn name(&self) -> &str {
125        "qcom-ssc-block-bus"
126    }
127
128    /// Performs the compatible operation.
129    fn compatible(&self) -> &[&str] {
130        COMPATIBLE
131    }
132
133    /// Performs the init operation.
134    fn init(&mut self, base: usize) -> Result<(), BusError> {
135        self.halt_regs.init(base, 0x10);
136        self.bus_init()?;
137        self.power_state = PowerState::On;
138        Ok(())
139    }
140
141    /// Performs the shutdown operation.
142    fn shutdown(&mut self) -> Result<(), BusError> {
143        let res = self.bus_deinit();
144        self.power_state = PowerState::Off;
145        res
146    }
147
148    /// Reads reg.
149    fn read_reg(&self, offset: usize) -> Result<u32, BusError> {
150        if !self.halt_regs.is_valid() {
151            return Err(BusError::InitFailed);
152        }
153        self.halt_regs.check_user_offset(offset)?;
154        Ok(self.halt_regs.read32(offset))
155    }
156
157    /// Writes reg.
158    fn write_reg(&mut self, offset: usize, value: u32) -> Result<(), BusError> {
159        if !self.halt_regs.is_valid() {
160            return Err(BusError::InitFailed);
161        }
162        self.halt_regs.check_user_offset(offset)?;
163        self.halt_regs.write32(offset, value);
164        Ok(())
165    }
166
167    /// Performs the children operation.
168    fn children(&self) -> Vec<BusChild> {
169        self.children.clone()
170    }
171}