Skip to main content

strat9_bus_drivers/
tegra_gmi.rs

1use crate::{BusChild, BusDriver, BusError, PowerState, mmio::MmioRegion};
2use alloc::{string::String, vec::Vec};
3
4const TEGRA_GMI_CONFIG: usize = 0x00;
5const TEGRA_GMI_TIMING0: usize = 0x10;
6const TEGRA_GMI_TIMING1: usize = 0x14;
7
8const CONFIG_GO: u32 = 1 << 31;
9const BUS_WIDTH_32BIT: u32 = 1 << 30;
10const MUX_MODE: u32 = 1 << 28;
11const RDY_BEFORE_DATA: u32 = 1 << 24;
12const RDY_ACTIVE_HIGH: u32 = 1 << 23;
13const ADV_ACTIVE_HIGH: u32 = 1 << 22;
14const OE_ACTIVE_HIGH: u32 = 1 << 21;
15const CS_ACTIVE_HIGH: u32 = 1 << 20;
16
17const MAX_CHIP_SELECT: u32 = 8;
18
19const COMPATIBLE: &[&str] = &["nvidia,tegra20-gmi", "nvidia,tegra30-gmi"];
20
21/// Performs the cs select operation.
22fn cs_select(x: u32) -> u32 {
23    (x & 0x7) << 4
24}
25/// Performs the muxed width operation.
26fn muxed_width(x: u32) -> u32 {
27    (x & 0xF) << 12
28}
29/// Performs the hold width operation.
30fn hold_width(x: u32) -> u32 {
31    (x & 0xF) << 8
32}
33/// Performs the adv width operation.
34fn adv_width(x: u32) -> u32 {
35    (x & 0xF) << 4
36}
37/// Performs the ce width operation.
38fn ce_width(x: u32) -> u32 {
39    x & 0xF
40}
41/// Performs the we width operation.
42fn we_width(x: u32) -> u32 {
43    (x & 0xFF) << 16
44}
45/// Performs the oe width operation.
46fn oe_width(x: u32) -> u32 {
47    (x & 0xFF) << 8
48}
49/// Performs the wait width operation.
50fn wait_width(x: u32) -> u32 {
51    x & 0xFF
52}
53
54pub struct GmiConfig {
55    pub bus_width_32: bool,
56    pub mux_mode: bool,
57    pub rdy_before_data: bool,
58    pub rdy_active_high: bool,
59    pub adv_active_high: bool,
60    pub oe_active_high: bool,
61    pub cs_active_high: bool,
62    pub chip_select: u32,
63}
64
65pub struct GmiTiming {
66    pub muxed_width: u32,
67    pub hold_width: u32,
68    pub adv_width: u32,
69    pub ce_width: u32,
70    pub we_width: u32,
71    pub oe_width: u32,
72    pub wait_width: u32,
73}
74
75pub struct TegraGmi {
76    regs: MmioRegion,
77    power_state: PowerState,
78    snor_config: u32,
79    snor_timing0: u32,
80    snor_timing1: u32,
81}
82
83impl TegraGmi {
84    /// Creates a new instance.
85    pub fn new() -> Self {
86        Self {
87            regs: MmioRegion::new(),
88            power_state: PowerState::Off,
89            snor_config: 0,
90            snor_timing0: 0,
91            snor_timing1: 0,
92        }
93    }
94
95    /// Performs the configure operation.
96    pub fn configure(&mut self, cfg: &GmiConfig, timing: &GmiTiming) {
97        let mut config = 0u32;
98        if cfg.bus_width_32 {
99            config |= BUS_WIDTH_32BIT;
100        }
101        if cfg.mux_mode {
102            config |= MUX_MODE;
103        }
104        if cfg.rdy_before_data {
105            config |= RDY_BEFORE_DATA;
106        }
107        if cfg.rdy_active_high {
108            config |= RDY_ACTIVE_HIGH;
109        }
110        if cfg.adv_active_high {
111            config |= ADV_ACTIVE_HIGH;
112        }
113        if cfg.oe_active_high {
114            config |= OE_ACTIVE_HIGH;
115        }
116        if cfg.cs_active_high {
117            config |= CS_ACTIVE_HIGH;
118        }
119        if cfg.chip_select < MAX_CHIP_SELECT {
120            config |= cs_select(cfg.chip_select);
121        }
122        self.snor_config = config;
123
124        self.snor_timing0 = muxed_width(timing.muxed_width)
125            | hold_width(timing.hold_width)
126            | adv_width(timing.adv_width)
127            | ce_width(timing.ce_width);
128
129        self.snor_timing1 =
130            we_width(timing.we_width) | oe_width(timing.oe_width) | wait_width(timing.wait_width);
131    }
132
133    /// Performs the apply config operation.
134    pub fn apply_config(&self) {
135        if !self.regs.is_valid() {
136            return;
137        }
138        self.regs.write32(TEGRA_GMI_TIMING0, self.snor_timing0);
139        self.regs.write32(TEGRA_GMI_TIMING1, self.snor_timing1);
140        self.regs
141            .write32(TEGRA_GMI_CONFIG, self.snor_config | CONFIG_GO);
142    }
143}
144
145impl BusDriver for TegraGmi {
146    /// Performs the name operation.
147    fn name(&self) -> &str {
148        "tegra-gmi"
149    }
150
151    /// Performs the compatible operation.
152    fn compatible(&self) -> &[&str] {
153        COMPATIBLE
154    }
155
156    /// Performs the init operation.
157    fn init(&mut self, base: usize) -> Result<(), BusError> {
158        self.regs.init(base, 0x20);
159        self.apply_config();
160        self.power_state = PowerState::On;
161        Ok(())
162    }
163
164    /// Performs the shutdown operation.
165    fn shutdown(&mut self) -> Result<(), BusError> {
166        self.power_state = PowerState::Off;
167        Ok(())
168    }
169
170    /// Performs the suspend operation.
171    fn suspend(&mut self) -> Result<(), BusError> {
172        self.power_state = PowerState::Suspended;
173        Ok(())
174    }
175
176    /// Performs the resume operation.
177    fn resume(&mut self) -> Result<(), BusError> {
178        self.apply_config();
179        self.power_state = PowerState::On;
180        Ok(())
181    }
182
183    /// Reads reg.
184    fn read_reg(&self, offset: usize) -> Result<u32, BusError> {
185        if !self.regs.is_valid() {
186            return Err(BusError::InitFailed);
187        }
188        Ok(self.regs.read32(offset))
189    }
190
191    /// Writes reg.
192    fn write_reg(&mut self, offset: usize, value: u32) -> Result<(), BusError> {
193        if !self.regs.is_valid() {
194            return Err(BusError::InitFailed);
195        }
196        self.regs.write32(offset, value);
197        Ok(())
198    }
199}