1use embedded_hal::digital::PinState;
3use zynq7000::gpio::{Gpio, MaskedOutput, MmioGpio};
4
5use crate::slcr::Slcr;
6
7use super::{PinIsOutputOnly, mio::MuxConfig};
8
9#[derive(Debug, Clone, Copy)]
10pub enum PinOffset {
11 Mio(usize),
12 Emio(usize),
13}
14
15impl PinOffset {
16 pub const fn new_for_mio(offset: usize) -> Option<Self> {
18 if offset > 53 {
19 return None;
20 }
21 Some(PinOffset::Mio(offset))
22 }
23
24 pub const fn new_for_emio(offset: usize) -> Option<Self> {
26 if offset > 63 {
27 return None;
28 }
29 Some(PinOffset::Emio(offset))
30 }
31
32 pub fn is_mio(&self) -> bool {
33 match self {
34 PinOffset::Mio(_) => true,
35 PinOffset::Emio(_) => false,
36 }
37 }
38}
39
40impl PinOffset {
41 pub fn offset(&self) -> usize {
42 match self {
43 PinOffset::Mio(offset) => *offset,
44 PinOffset::Emio(offset) => *offset,
45 }
46 }
47}
48
49pub struct LowLevelGpio {
50 offset: PinOffset,
51 regs: MmioGpio<'static>,
52}
53
54impl LowLevelGpio {
55 pub fn new(offset: PinOffset) -> Self {
56 Self {
57 offset,
58 regs: unsafe { Gpio::new_mmio_fixed() },
59 }
60 }
61
62 pub fn offset(&self) -> PinOffset {
63 self.offset
64 }
65
66 pub fn configure_as_output_push_pull(&mut self, init_level: PinState) {
68 let (offset, dirm, outen) = self.get_dirm_outen_regs_and_local_offset();
69 if self.offset.is_mio() {
70 self.reconfigure_slcr_mio_cfg(false, None, Some(MuxConfig::new_for_gpio()));
72 }
73 let mut curr_dirm = unsafe { core::ptr::read_volatile(dirm) };
74 curr_dirm |= 1 << offset;
75 unsafe { core::ptr::write_volatile(dirm, curr_dirm) };
76 let mut curr_outen = unsafe { core::ptr::read_volatile(outen) };
77 curr_outen |= 1 << offset;
78 unsafe { core::ptr::write_volatile(outen, curr_outen) };
79 self.write_state(init_level);
81 }
82
83 pub fn configure_as_output_open_drain(
93 &mut self,
94 init_level: PinState,
95 with_internal_pullup: bool,
96 ) {
97 let (offset, dirm, outen) = self.get_dirm_outen_regs_and_local_offset();
98 if self.offset.is_mio() {
99 self.reconfigure_slcr_mio_cfg(
101 false,
102 Some(with_internal_pullup),
103 Some(MuxConfig::new_for_gpio()),
104 );
105 }
106 let mut curr_dirm = unsafe { core::ptr::read_volatile(dirm) };
107 curr_dirm |= 1 << offset;
108 unsafe { core::ptr::write_volatile(dirm, curr_dirm) };
109 let mut curr_outen = unsafe { core::ptr::read_volatile(outen) };
111 if init_level == PinState::High {
112 curr_outen &= !(1 << offset);
113 } else {
114 curr_outen |= 1 << offset;
115 self.write_state(init_level);
116 }
117 unsafe { core::ptr::write_volatile(outen, curr_outen) };
118 }
119
120 pub fn configure_as_input_floating(&mut self) -> Result<(), PinIsOutputOnly> {
122 if self.offset.is_mio() {
123 let offset_raw = self.offset.offset();
124 if offset_raw == 7 || offset_raw == 8 {
125 return Err(PinIsOutputOnly);
126 }
127 self.reconfigure_slcr_mio_cfg(true, Some(false), Some(MuxConfig::new_for_gpio()));
128 }
129 self.configure_input_pin();
130 Ok(())
131 }
132
133 pub fn configure_as_input_with_pull_up(&mut self) -> Result<(), PinIsOutputOnly> {
135 if self.offset.is_mio() {
136 let offset_raw = self.offset.offset();
137 if offset_raw == 7 || offset_raw == 8 {
138 return Err(PinIsOutputOnly);
139 }
140 self.reconfigure_slcr_mio_cfg(true, Some(true), Some(MuxConfig::new_for_gpio()));
141 }
142 self.configure_input_pin();
143 Ok(())
144 }
145
146 pub fn configure_as_io_periph_pin(&mut self, mux_conf: MuxConfig, pullup: Option<bool>) {
148 self.reconfigure_slcr_mio_cfg(false, pullup, Some(mux_conf));
149 }
150
151 pub fn set_mio_pin_config(&mut self, config: zynq7000::slcr::mio::Config) {
152 let raw_offset = self.offset.offset();
153 let mut slcr_wrapper = unsafe { Slcr::steal() };
155 slcr_wrapper.modify(|mut_slcr| mut_slcr.write_mio_pins(raw_offset, config).unwrap());
156 }
157
158 pub fn set_mio_pin_config_with_unlocked_slcr(
160 &mut self,
161 slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
162 config: zynq7000::slcr::mio::Config,
163 ) {
164 let raw_offset = self.offset.offset();
165 slcr.write_mio_pins(raw_offset, config).unwrap();
166 }
167
168 #[inline]
169 pub fn is_low(&self) -> bool {
170 let (offset, in_reg) = self.get_data_in_reg_and_local_offset();
171 let in_val = unsafe { core::ptr::read_volatile(in_reg) };
172 ((in_val >> offset) & 0b1) == 0
173 }
174
175 #[inline]
176 pub fn is_high(&self) -> bool {
177 !self.is_low()
178 }
179
180 #[inline]
181 pub fn is_set_low(&self) -> bool {
182 let (offset, out_reg) = self.get_data_out_reg_and_local_offset();
183 let out_val = unsafe { core::ptr::read_volatile(out_reg) };
184 ((out_val >> offset) & 0b1) == 0
185 }
186
187 #[inline]
188 pub fn is_set_high(&self) -> bool {
189 !self.is_set_low()
190 }
191
192 #[inline]
193 pub fn enable_output_driver(&mut self) {
194 let (offset, _dirm, outen) = self.get_dirm_outen_regs_and_local_offset();
195 let mut outen_reg = unsafe { core::ptr::read_volatile(outen) };
196 outen_reg |= 1 << offset;
197 unsafe { core::ptr::write_volatile(outen, outen_reg) };
198 }
199
200 #[inline]
201 pub fn disable_output_driver(&mut self) {
202 let (offset, _dirm, outen) = self.get_dirm_outen_regs_and_local_offset();
203 let mut outen_reg = unsafe { core::ptr::read_volatile(outen) };
204 outen_reg &= !(1 << offset);
205 unsafe { core::ptr::write_volatile(outen, outen_reg) };
206 }
207
208 #[inline]
209 pub fn set_low(&mut self) {
210 self.write_state(PinState::Low)
211 }
212
213 #[inline]
214 pub fn set_high(&mut self) {
215 self.write_state(PinState::High)
216 }
217
218 #[inline]
219 fn write_state(&mut self, level: PinState) {
220 let (offset_in_reg, masked_out_ptr) = self.get_masked_out_reg_and_local_offset();
221 unsafe {
222 core::ptr::write_volatile(
223 masked_out_ptr,
224 MaskedOutput::builder()
225 .with_mask(!(1 << offset_in_reg))
226 .with_output((level as u16) << offset_in_reg)
227 .build(),
228 );
229 }
230 }
231
232 fn reconfigure_slcr_mio_cfg(
233 &mut self,
234 tristate: bool,
235 pullup: Option<bool>,
236 mux_conf: Option<MuxConfig>,
237 ) {
238 let raw_offset = self.offset.offset();
239 let mut slcr_wrapper = unsafe { Slcr::steal() };
241 let mio_cfg = slcr_wrapper.regs().read_mio_pins(raw_offset).unwrap();
245 if (pullup.is_some() && mio_cfg.pullup() != pullup.unwrap())
246 || (mux_conf.is_some() && MuxConfig::from(mio_cfg) != mux_conf.unwrap())
247 || tristate != mio_cfg.tri_enable()
248 {
249 slcr_wrapper.modify(|mut_slcr| {
250 mut_slcr
251 .modify_mio_pins(raw_offset, |mut val| {
252 if let Some(pullup) = pullup {
253 val.set_pullup(pullup);
254 }
255 if let Some(mux_conf) = mux_conf {
256 val.set_l0_sel(mux_conf.l0_sel());
257 val.set_l1_sel(mux_conf.l1_sel());
258 val.set_l2_sel(mux_conf.l2_sel());
259 val.set_l3_sel(mux_conf.l3_sel());
260 }
261 val.set_tri_enable(tristate);
262 val
263 })
264 .unwrap();
265 });
266 }
267 }
268
269 fn configure_input_pin(&mut self) {
270 let (offset, dirm, outen) = self.get_dirm_outen_regs_and_local_offset();
271 let mut curr_dirm = unsafe { core::ptr::read_volatile(dirm) };
272 curr_dirm &= !(1 << offset);
273 unsafe { core::ptr::write_volatile(dirm, curr_dirm) };
274 let mut curr_outen = unsafe { core::ptr::read_volatile(outen) };
275 curr_outen &= !(1 << offset);
276 unsafe { core::ptr::write_volatile(outen, curr_outen) };
277 }
278
279 #[inline(always)]
280 fn get_data_in_reg_and_local_offset(&self) -> (usize, *mut u32) {
281 match self.offset {
282 PinOffset::Mio(offset) => match offset {
283 0..=31 => (offset, self.regs.pointer_to_in_0()),
284 32..=53 => (offset - 32, self.regs.pointer_to_in_1()),
285 _ => panic!("invalid MIO pin offset"),
286 },
287 PinOffset::Emio(offset) => match offset {
288 0..=31 => (offset, self.regs.pointer_to_in_2()),
289 32..=63 => (offset - 32, self.regs.pointer_to_in_3()),
290 _ => panic!("invalid EMIO pin offset"),
291 },
292 }
293 }
294
295 #[inline(always)]
296 fn get_data_out_reg_and_local_offset(&self) -> (usize, *mut u32) {
297 match self.offset {
298 PinOffset::Mio(offset) => match offset {
299 0..=31 => (offset, self.regs.pointer_to_out_0()),
300 32..=53 => (offset - 32, self.regs.pointer_to_out_1()),
301 _ => panic!("invalid MIO pin offset"),
302 },
303 PinOffset::Emio(offset) => match offset {
304 0..=31 => (offset, self.regs.pointer_to_out_2()),
305 32..=63 => (offset - 32, self.regs.pointer_to_out_3()),
306 _ => panic!("invalid EMIO pin offset"),
307 },
308 }
309 }
310
311 #[inline(always)]
312 fn get_dirm_outen_regs_and_local_offset(&self) -> (usize, *mut u32, *mut u32) {
313 match self.offset {
314 PinOffset::Mio(offset) => match offset {
315 0..=31 => (
316 offset,
317 self.regs.bank_0_shared().pointer_to_dirm(),
318 self.regs.bank_0_shared().pointer_to_out_en(),
319 ),
320 32..=53 => (
321 offset - 32,
322 self.regs.bank_1_shared().pointer_to_dirm(),
323 self.regs.bank_1_shared().pointer_to_out_en(),
324 ),
325 _ => panic!("invalid MIO pin offset"),
326 },
327 PinOffset::Emio(offset) => match offset {
328 0..=31 => (
329 offset,
330 self.regs.bank_2_shared().pointer_to_dirm(),
331 self.regs.bank_2_shared().pointer_to_out_en(),
332 ),
333 32..=63 => (
334 offset - 32,
335 self.regs.bank_3_shared().pointer_to_dirm(),
336 self.regs.bank_3_shared().pointer_to_out_en(),
337 ),
338 _ => panic!("invalid EMIO pin offset"),
339 },
340 }
341 }
342
343 #[inline(always)]
344 fn get_masked_out_reg_and_local_offset(&mut self) -> (usize, *mut MaskedOutput) {
345 match self.offset {
346 PinOffset::Mio(offset) => match offset {
347 0..=15 => (offset, self.regs.pointer_to_masked_out_0_lsw()),
348 16..=31 => (offset - 16, self.regs.pointer_to_masked_out_0_msw()),
349 32..=47 => (offset - 32, self.regs.pointer_to_masked_out_1_lsw()),
350 48..=53 => (offset - 48, self.regs.pointer_to_masked_out_1_msw()),
351 _ => panic!("invalid MIO pin offset"),
352 },
353 PinOffset::Emio(offset) => match offset {
354 0..=15 => (offset, self.regs.pointer_to_masked_out_2_lsw()),
355 16..=31 => (offset - 16, self.regs.pointer_to_masked_out_2_msw()),
356 32..=47 => (offset - 32, self.regs.pointer_to_masked_out_3_lsw()),
357 48..=63 => (offset - 48, self.regs.pointer_to_masked_out_3_msw()),
358 _ => panic!("invalid EMIO pin offset"),
359 },
360 }
361 }
362}