Skip to main content

strat9_bus_drivers/
qcom_ebi2.rs

1use crate::{BusChild, BusDriver, BusError, PowerState, mmio::MmioRegion};
2use alloc::{string::String, vec::Vec};
3
4const EBI2_XMEM_CFG: usize = 0x0000;
5const EBI2_XMEM_CS0_SLOW_CFG: usize = 0x0008;
6const EBI2_XMEM_CS1_SLOW_CFG: usize = 0x000C;
7const EBI2_XMEM_CS2_SLOW_CFG: usize = 0x0010;
8const EBI2_XMEM_CS3_SLOW_CFG: usize = 0x0014;
9const EBI2_XMEM_CS4_SLOW_CFG: usize = 0x0018;
10const EBI2_XMEM_CS5_SLOW_CFG: usize = 0x001C;
11const EBI2_XMEM_CS0_FAST_CFG: usize = 0x0028;
12const EBI2_XMEM_CS1_FAST_CFG: usize = 0x002C;
13const EBI2_XMEM_CS2_FAST_CFG: usize = 0x0030;
14const EBI2_XMEM_CS3_FAST_CFG: usize = 0x0034;
15const EBI2_XMEM_CS4_FAST_CFG: usize = 0x0038;
16const EBI2_XMEM_CS5_FAST_CFG: usize = 0x003C;
17
18const CS0_ENABLE: u32 = 0x03;
19const CS1_ENABLE: u32 = 0x0C;
20const CS2_ENABLE: u32 = 0x10;
21const CS3_ENABLE: u32 = 0x20;
22const CS4_ENABLE: u32 = 0x180;
23const CS5_ENABLE: u32 = 0x600;
24
25const SLOW_RECOVERY_SHIFT: u32 = 28;
26const SLOW_WR_HOLD_SHIFT: u32 = 24;
27const SLOW_WR_DELTA_SHIFT: u32 = 16;
28const SLOW_RD_DELTA_SHIFT: u32 = 8;
29const SLOW_WR_WAIT_SHIFT: u32 = 4;
30const SLOW_RD_WAIT_SHIFT: u32 = 0;
31
32const FAST_RD_HOLD_SHIFT: u32 = 24;
33const FAST_ADV_OE_RECOVERY_SHIFT: u32 = 16;
34const FAST_ADDR_HOLD_ENA: u32 = 1 << 5;
35
36const NUM_CS: usize = 6;
37
38const CS_ENABLE_MASKS: [u32; NUM_CS] = [
39    CS0_ENABLE, CS1_ENABLE, CS2_ENABLE, CS3_ENABLE, CS4_ENABLE, CS5_ENABLE,
40];
41const CS_SLOW_OFFSETS: [usize; NUM_CS] = [
42    EBI2_XMEM_CS0_SLOW_CFG,
43    EBI2_XMEM_CS1_SLOW_CFG,
44    EBI2_XMEM_CS2_SLOW_CFG,
45    EBI2_XMEM_CS3_SLOW_CFG,
46    EBI2_XMEM_CS4_SLOW_CFG,
47    EBI2_XMEM_CS5_SLOW_CFG,
48];
49const CS_FAST_OFFSETS: [usize; NUM_CS] = [
50    EBI2_XMEM_CS0_FAST_CFG,
51    EBI2_XMEM_CS1_FAST_CFG,
52    EBI2_XMEM_CS2_FAST_CFG,
53    EBI2_XMEM_CS3_FAST_CFG,
54    EBI2_XMEM_CS4_FAST_CFG,
55    EBI2_XMEM_CS5_FAST_CFG,
56];
57
58const COMPATIBLE: &[&str] = &["qcom,msm8660-ebi2", "qcom,apq8060-ebi2"];
59
60pub struct Ebi2CsConfig {
61    pub recovery_cycles: u32,
62    pub wr_hold_cycles: u32,
63    pub wr_delta_cycles: u32,
64    pub rd_delta_cycles: u32,
65    pub wr_wait_cycles: u32,
66    pub rd_wait_cycles: u32,
67    pub rd_hold_cycles: u32,
68    pub adv_oe_recovery: u32,
69    pub addr_hold_ena: bool,
70}
71
72impl Ebi2CsConfig {
73    /// Converts this to slow reg.
74    pub fn to_slow_reg(&self) -> u32 {
75        (self.recovery_cycles << SLOW_RECOVERY_SHIFT)
76            | (self.wr_hold_cycles << SLOW_WR_HOLD_SHIFT)
77            | (self.wr_delta_cycles << SLOW_WR_DELTA_SHIFT)
78            | (self.rd_delta_cycles << SLOW_RD_DELTA_SHIFT)
79            | (self.wr_wait_cycles << SLOW_WR_WAIT_SHIFT)
80            | (self.rd_wait_cycles << SLOW_RD_WAIT_SHIFT)
81    }
82
83    /// Converts this to fast reg.
84    pub fn to_fast_reg(&self) -> u32 {
85        let mut val = (self.rd_hold_cycles << FAST_RD_HOLD_SHIFT)
86            | (self.adv_oe_recovery << FAST_ADV_OE_RECOVERY_SHIFT);
87        if self.addr_hold_ena {
88            val |= FAST_ADDR_HOLD_ENA;
89        }
90        val
91    }
92}
93
94pub struct QcomEbi2 {
95    regs: MmioRegion,
96    cs_configs: [Option<Ebi2CsConfig>; NUM_CS],
97    power_state: PowerState,
98    children: Vec<BusChild>,
99}
100
101impl QcomEbi2 {
102    /// Creates a new instance.
103    pub fn new() -> Self {
104        Self {
105            regs: MmioRegion::new(),
106            cs_configs: [const { None }; NUM_CS],
107            power_state: PowerState::Off,
108            children: Vec::new(),
109        }
110    }
111
112    /// Performs the configure cs operation.
113    pub fn configure_cs(&mut self, cs: usize, config: Ebi2CsConfig) {
114        if cs < NUM_CS {
115            self.cs_configs[cs] = Some(config);
116        }
117    }
118
119    /// Performs the apply config operation.
120    fn apply_config(&self) {
121        self.regs.write32(EBI2_XMEM_CFG, 0);
122
123        for cs in 0..NUM_CS {
124            if let Some(ref cfg) = self.cs_configs[cs] {
125                let mut xmem = self.regs.read32(EBI2_XMEM_CFG);
126                xmem |= CS_ENABLE_MASKS[cs];
127                self.regs.write32(EBI2_XMEM_CFG, xmem);
128
129                self.regs.write32(CS_SLOW_OFFSETS[cs], cfg.to_slow_reg());
130                self.regs.write32(CS_FAST_OFFSETS[cs], cfg.to_fast_reg());
131            }
132        }
133    }
134
135    /// Performs the add child operation.
136    pub fn add_child(&mut self, child: BusChild) {
137        self.children.push(child);
138    }
139}
140
141impl BusDriver for QcomEbi2 {
142    /// Performs the name operation.
143    fn name(&self) -> &str {
144        "qcom-ebi2"
145    }
146
147    /// Performs the compatible operation.
148    fn compatible(&self) -> &[&str] {
149        COMPATIBLE
150    }
151
152    /// Performs the init operation.
153    fn init(&mut self, base: usize) -> Result<(), BusError> {
154        self.regs.init(base, 0x100);
155        self.apply_config();
156        self.power_state = PowerState::On;
157        Ok(())
158    }
159
160    /// Performs the shutdown operation.
161    fn shutdown(&mut self) -> Result<(), BusError> {
162        self.power_state = PowerState::Off;
163        Ok(())
164    }
165
166    /// Reads reg.
167    fn read_reg(&self, offset: usize) -> Result<u32, BusError> {
168        if !self.regs.is_valid() {
169            return Err(BusError::InitFailed);
170        }
171        Ok(self.regs.read32(offset))
172    }
173
174    /// Writes reg.
175    fn write_reg(&mut self, offset: usize, value: u32) -> Result<(), BusError> {
176        if !self.regs.is_valid() {
177            return Err(BusError::InitFailed);
178        }
179        self.regs.write32(offset, value);
180        Ok(())
181    }
182
183    /// Performs the children operation.
184    fn children(&self) -> Vec<BusChild> {
185        self.children.clone()
186    }
187}