1pub mod emio;
12pub mod ll;
13pub mod mio;
14
15use core::convert::Infallible;
16use ll::PinOffset;
17use mio::{MioPin, MuxConfig};
18
19use crate::gpio::ll::LowLevelGpio;
20use crate::{enable_amba_peripheral_clock, slcr::Slcr};
21pub use embedded_hal::digital::PinState;
22use zynq7000::{gpio::MmioGpio, slcr::reset::GpioClockReset};
23
24#[derive(Debug, thiserror::Error)]
25#[error("MIO pins 7 and 8 can only be output pins")]
26pub struct PinIsOutputOnly;
27
28pub struct GpioPins {
30 pub mio: mio::Pins,
31 pub emio: emio::Pins,
32}
33
34impl GpioPins {
35 pub fn new(gpio: MmioGpio) -> Self {
36 enable_amba_peripheral_clock(crate::PeriphSelect::Gpio);
37 Self {
38 mio: mio::Pins::new(unsafe { gpio.clone() }),
39 emio: emio::Pins::new(gpio),
40 }
41 }
42}
43
44#[inline]
46pub fn reset() {
47 unsafe {
48 Slcr::with(|regs| {
49 regs.reset_ctrl()
50 .write_gpio(GpioClockReset::builder().with_gpio_cpu1x_rst(true).build());
51 cortex_ar::asm::nop();
53 regs.reset_ctrl()
54 .write_gpio(GpioClockReset::builder().with_gpio_cpu1x_rst(false).build());
55 });
56 }
57}
58
59#[derive(Debug, Copy, Clone, PartialEq, Eq)]
61pub enum PinMode {
62 OutputPushPull,
63 OutputOpenDrain,
66 InputFloating,
67 InputPullUp,
68 MioIoPeriph(MuxConfig),
70}
71
72#[derive(Debug, thiserror::Error)]
73#[error("invalid pin mode for MIO pin: {0:?}")]
74pub struct InvalidPinMode(pub PinMode);
75
76impl embedded_hal::digital::Error for InvalidPinMode {
77 fn kind(&self) -> embedded_hal::digital::ErrorKind {
78 embedded_hal::digital::ErrorKind::Other
79 }
80}
81
82pub trait IoPinProvider {
83 fn mode(&self) -> PinMode;
84
85 fn offset(&self) -> PinOffset;
86
87 #[inline]
88 fn is_input(&self) -> bool {
89 matches!(self.mode(), PinMode::InputFloating | PinMode::InputPullUp)
90 }
91 #[inline]
92 fn is_output(&self) -> bool {
93 matches!(
94 self.mode(),
95 PinMode::OutputPushPull | PinMode::OutputOpenDrain
96 )
97 }
98
99 #[inline]
100 fn is_io_periph(&self) -> bool {
101 matches!(self.mode(), PinMode::MioIoPeriph(_))
102 }
103}
104
105pub struct Flex {
131 ll: LowLevelGpio,
132 mode: PinMode,
133}
134
135impl Flex {
136 pub fn new_for_mio<I: mio::PinId>(_pin: mio::Pin<I>) -> Self {
137 let mut ll = LowLevelGpio::new(PinOffset::Mio(I::OFFSET));
138 if I::OFFSET == 7 || I::OFFSET == 8 {
139 ll.configure_as_output_push_pull(PinState::Low);
140 } else {
141 ll.configure_as_input_floating().unwrap();
142 }
143 Self {
144 ll,
145 mode: PinMode::InputFloating,
146 }
147 }
148
149 pub fn new_for_emio(pin: emio::EmioPin) -> Self {
150 let mut ll = LowLevelGpio::new(PinOffset::new_for_emio(pin.offset()).unwrap());
151 ll.configure_as_input_floating().unwrap();
152 Self {
153 ll,
154 mode: PinMode::InputFloating,
155 }
156 }
157
158 pub fn configure_as_input_floating(&mut self) -> Result<(), PinIsOutputOnly> {
159 self.mode = PinMode::InputFloating;
160 self.ll.configure_as_input_floating()
161 }
162
163 pub fn configure_as_input_with_pull_up(&mut self) -> Result<(), PinIsOutputOnly> {
164 self.mode = PinMode::InputPullUp;
165 self.ll.configure_as_input_with_pull_up()
166 }
167
168 pub fn configure_as_output_push_pull(&mut self, level: PinState) {
169 self.mode = PinMode::OutputPushPull;
170 self.ll.configure_as_output_push_pull(level);
171 }
172
173 pub fn configure_as_output_open_drain(&mut self, level: PinState, with_internal_pullup: bool) {
174 self.mode = PinMode::OutputOpenDrain;
175 self.ll
176 .configure_as_output_open_drain(level, with_internal_pullup);
177 }
178
179 pub fn set_high(&mut self) {
181 if self.is_input() {
182 return;
183 }
184 if self.mode == PinMode::OutputOpenDrain {
185 self.ll.disable_output_driver();
186 } else {
187 self.ll.set_high();
188 }
189 }
190
191 pub fn set_low(&mut self) {
193 if self.is_input() {
194 return;
195 }
196 self.ll.set_low();
197 if self.mode == PinMode::OutputOpenDrain {
198 self.ll.enable_output_driver();
199 }
200 }
201
202 #[inline]
204 pub fn is_high(&self) -> bool {
205 self.ll.is_high()
206 }
207
208 #[inline]
210 pub fn is_low(&self) -> bool {
211 !self.ll.is_high()
212 }
213
214 #[inline]
217 pub fn is_set_low(&self) -> bool {
218 self.ll.is_set_low()
219 }
220
221 #[inline]
224 pub fn is_set_high(&self) -> bool {
225 !self.is_set_low()
226 }
227}
228
229impl IoPinProvider for Flex {
230 fn mode(&self) -> PinMode {
231 self.mode
232 }
233
234 fn offset(&self) -> PinOffset {
235 self.ll.offset()
236 }
237}
238
239impl embedded_hal::digital::ErrorType for Flex {
240 type Error = Infallible;
241}
242
243impl embedded_hal::digital::InputPin for Flex {
244 #[inline]
246 fn is_high(&mut self) -> Result<bool, Self::Error> {
247 Ok(self.ll.is_high())
248 }
249
250 #[inline]
252 fn is_low(&mut self) -> Result<bool, Self::Error> {
253 Ok(self.ll.is_low())
254 }
255}
256
257impl embedded_hal::digital::OutputPin for Flex {
258 #[inline]
260 fn set_low(&mut self) -> Result<(), Self::Error> {
261 self.set_low();
262 Ok(())
263 }
264
265 #[inline]
267 fn set_high(&mut self) -> Result<(), Self::Error> {
268 self.set_high();
269 Ok(())
270 }
271}
272
273impl embedded_hal::digital::StatefulOutputPin for Flex {
274 #[inline]
277 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
278 Ok(self.ll.is_set_high())
279 }
280
281 #[inline]
284 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
285 Ok(self.ll.is_set_low())
286 }
287}
288
289pub struct Output(LowLevelGpio);
291
292impl Output {
293 pub fn new_for_mio<I: mio::PinId>(_pin: mio::Pin<I>, init_level: PinState) -> Self {
294 let mut low_level = LowLevelGpio::new(PinOffset::Mio(I::OFFSET));
295 low_level.configure_as_output_push_pull(init_level);
296 Self(low_level)
297 }
298
299 pub fn new_for_emio(pin: emio::EmioPin, init_level: PinState) -> Self {
300 let mut low_level = LowLevelGpio::new(PinOffset::new_for_emio(pin.offset()).unwrap());
301 low_level.configure_as_output_push_pull(init_level);
302 Self(low_level)
303 }
304
305 #[inline]
306 pub fn set_low(&mut self) {
307 self.0.set_low();
308 }
309
310 #[inline]
311 pub fn set_high(&mut self) {
312 self.0.set_high();
313 }
314}
315
316impl embedded_hal::digital::ErrorType for Output {
317 type Error = Infallible;
318}
319
320impl embedded_hal::digital::OutputPin for Output {
321 fn set_low(&mut self) -> Result<(), Self::Error> {
322 self.0.set_low();
323 Ok(())
324 }
325 fn set_high(&mut self) -> Result<(), Self::Error> {
326 self.0.set_high();
327 Ok(())
328 }
329}
330
331impl embedded_hal::digital::StatefulOutputPin for Output {
332 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
333 Ok(self.0.is_set_high())
334 }
335
336 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
337 Ok(self.0.is_set_low())
338 }
339}
340
341pub struct Input(LowLevelGpio);
343
344impl Input {
345 pub fn new_for_mio<I: mio::PinId>(_pin: mio::Pin<I>) -> Result<Self, PinIsOutputOnly> {
346 let mut low_level = LowLevelGpio::new(PinOffset::Mio(I::OFFSET));
347 low_level.configure_as_input_floating()?;
348 Ok(Self(low_level))
349 }
350
351 pub fn new_for_emio(pin: emio::EmioPin) -> Result<Self, PinIsOutputOnly> {
352 let mut low_level = LowLevelGpio::new(PinOffset::new_for_emio(pin.offset()).unwrap());
353 low_level.configure_as_input_floating()?;
354 Ok(Self(low_level))
355 }
356
357 pub fn is_high(&self) -> bool {
358 self.0.is_high()
359 }
360
361 pub fn is_low(&self) -> bool {
362 self.0.is_low()
363 }
364}
365
366impl embedded_hal::digital::ErrorType for Input {
367 type Error = Infallible;
368}
369
370impl embedded_hal::digital::InputPin for Input {
371 fn is_high(&mut self) -> Result<bool, Self::Error> {
372 Ok(self.0.is_high())
373 }
374
375 fn is_low(&mut self) -> Result<bool, Self::Error> {
376 Ok(self.0.is_low())
377 }
378}
379
380pub struct IoPeriphPin {
382 pin: LowLevelGpio,
383 mux_conf: MuxConfig,
384}
385
386impl IoPeriphPin {
387 pub fn new(pin: impl MioPin, mux_conf: MuxConfig, pullup: Option<bool>) -> Self {
390 let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
391 low_level.configure_as_io_periph_pin(mux_conf, pullup);
392 Self {
393 pin: low_level,
394 mux_conf,
395 }
396 }
397
398 pub fn new_with_full_config(pin: impl MioPin, config: zynq7000::slcr::mio::Config) -> Self {
400 let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
401 low_level.set_mio_pin_config(config);
402 Self {
403 pin: low_level,
404 mux_conf: MuxConfig::new(
405 config.l0_sel(),
406 config.l1_sel(),
407 config.l2_sel(),
408 config.l3_sel(),
409 ),
410 }
411 }
412
413 pub fn new_with_full_config_and_unlocked_slcr(
415 pin: impl MioPin,
416 slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
417 config: zynq7000::slcr::mio::Config,
418 ) -> Self {
419 let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
420 low_level.set_mio_pin_config_with_unlocked_slcr(slcr, config);
421 Self {
422 pin: low_level,
423 mux_conf: MuxConfig::new(
424 config.l0_sel(),
425 config.l1_sel(),
426 config.l2_sel(),
427 config.l3_sel(),
428 ),
429 }
430 }
431}
432
433impl IoPinProvider for IoPeriphPin {
434 #[inline]
435 fn mode(&self) -> PinMode {
436 PinMode::MioIoPeriph(self.mux_conf)
437 }
438
439 #[inline]
440 fn offset(&self) -> PinOffset {
441 self.pin.offset()
442 }
443}