1use kernel::hil::gpio;
8use kernel::hil::gpio::{Configuration, Configure, FloatingState};
9use kernel::utilities::registers::interfaces::{Readable, Writeable};
10use kernel::utilities::registers::{register_bitfields, FieldValue, LocalRegisterCopy};
11use kernel::utilities::StaticRef;
12
13use crate::registers::pinmux_regs::{
14    PinmuxRegisters, DIO_PAD_ATTR_REGWEN, MIO_OUTSEL_REGWEN, MIO_PAD_ATTR_REGWEN,
15    MIO_PERIPH_INSEL_REGWEN,
16};
17use crate::registers::top_earlgrey::{
18    DirectPads, MuxedPads, PinmuxInsel, PinmuxOutsel, PinmuxPeripheralIn, PINMUX_AON_BASE_ADDR,
19    PINMUX_MIO_PERIPH_INSEL_IDX_OFFSET,
20};
21
22pub const PINMUX_BASE: StaticRef<PinmuxRegisters> =
23    unsafe { StaticRef::new(PINMUX_AON_BASE_ADDR as *const PinmuxRegisters) };
24
25register_bitfields![u32,
32    pub(crate) PAD_ATTR [
33        INVERT OFFSET(0) NUMBITS(1) [],
34        VIRTUAL_OPEN_DRAIN_EN OFFSET(1) NUMBITS(1) [],
35        PULL_EN OFFSET(2) NUMBITS(1) [],
36        PULL OFFSET(3) NUMBITS(1) [
37            DOWN = 0,
38            UP = 1,
39        ],
40        KEEPER_EN OFFSET(4) NUMBITS(1) [],
41        SCHMITT_EN OFFSET(5) NUMBITS(1) [],
42        OPEN_DRAIN_EN OFFSET(6) NUMBITS(1) [],
43        SLEW_RATE OFFSET(16) NUMBITS(2) [],
44        DRIVE_STRENGTH OFFSET(20) NUMBITS(4) [],
45    ],
46];
47
48type PadAttribute = LocalRegisterCopy<u32, PAD_ATTR::Register>;
49
50#[derive(Copy, Clone, PartialEq, Eq)]
51pub enum Pad {
52    Mio(MuxedPads),
53    Dio(DirectPads),
54}
55
56impl Pad {
57    fn pad_attr(&self) -> PadAttribute {
59        PadAttribute::new(match *self {
60            Self::Mio(mio) => PINMUX_BASE.mio_pad_attr[mio as usize].get(),
61            Self::Dio(dio) => PINMUX_BASE.dio_pad_attr[dio as usize].get(),
62        })
63    }
64
65    fn modify_pad_attr(&self, flags: FieldValue<u32, PAD_ATTR::Register>) {
67        let mut attr = self.pad_attr();
68        attr.modify(flags);
69        match *self {
70            Self::Mio(mio) => &PINMUX_BASE.mio_pad_attr[mio as usize].set(attr.get()),
71            Self::Dio(dio) => &PINMUX_BASE.dio_pad_attr[dio as usize].set(attr.get()),
72        };
73    }
74
75    pub fn set_floating_state(&self, mode: gpio::FloatingState) {
76        self.modify_pad_attr(match mode {
77            gpio::FloatingState::PullUp => PAD_ATTR::PULL_EN::SET + PAD_ATTR::PULL::UP,
78            gpio::FloatingState::PullDown => PAD_ATTR::PULL_EN::SET + PAD_ATTR::PULL::DOWN,
79            gpio::FloatingState::PullNone => PAD_ATTR::PULL_EN::CLEAR + PAD_ATTR::PULL::CLEAR,
80        });
81    }
82
83    pub fn set_output_open_drain(&self) {
84        self.modify_pad_attr(PAD_ATTR::OPEN_DRAIN_EN::SET);
85    }
86
87    pub fn set_output_push_pull(&self) {
88        self.modify_pad_attr(PAD_ATTR::OPEN_DRAIN_EN::CLEAR);
89    }
90
91    pub fn set_invert_sense(&self, invert: bool) {
92        if invert {
93            self.modify_pad_attr(PAD_ATTR::INVERT::SET)
94        } else {
95            self.modify_pad_attr(PAD_ATTR::INVERT::CLEAR)
96        }
97    }
98
99    pub fn floating_state(&self) -> gpio::FloatingState {
100        let pad_attr: PadAttribute = self.pad_attr();
101        if pad_attr.matches_all(PAD_ATTR::PULL::UP + PAD_ATTR::PULL_EN::SET) {
102            gpio::FloatingState::PullUp
103        } else if pad_attr.matches_all(PAD_ATTR::PULL::DOWN + PAD_ATTR::PULL_EN::SET) {
104            gpio::FloatingState::PullDown
105        } else {
106            gpio::FloatingState::PullNone
107        }
108    }
109
110    pub fn lock_pad_attributes(&self) {
112        match *self {
113            Self::Mio(mio) => PINMUX_BASE.mio_pad_attr_regwen[(mio as u32) as usize]
114                .write(MIO_PAD_ATTR_REGWEN::EN_0::CLEAR),
115            Self::Dio(dio) => PINMUX_BASE.dio_pad_attr_regwen[(dio as u32) as usize]
116                .write(DIO_PAD_ATTR_REGWEN::EN_0::CLEAR),
117        }
118    }
119}
120
121pub trait SelectOutput {
126    fn connect_output(self, output: PinmuxOutsel);
128
129    fn connect_low(self);
131
132    fn connect_high(self);
134
135    fn connect_high_z(self);
138
139    fn lock(self);
141
142    fn get_selector(self) -> PinmuxOutsel;
144}
145
146impl SelectOutput for MuxedPads {
148    fn connect_output(self, output: PinmuxOutsel) {
149        PINMUX_BASE.mio_outsel[self as usize].set(output as u32)
150    }
151
152    fn connect_low(self) {
153        PINMUX_BASE.mio_outsel[self as usize].set(PinmuxOutsel::ConstantZero as u32)
154    }
155
156    fn connect_high(self) {
157        PINMUX_BASE.mio_outsel[self as usize].set(PinmuxOutsel::ConstantOne as u32)
158    }
159
160    fn connect_high_z(self) {
161        PINMUX_BASE.mio_outsel[self as usize].set(PinmuxOutsel::ConstantHighZ as u32)
162    }
163
164    fn lock(self) {
165        PINMUX_BASE.mio_outsel_regwen[self as usize].write(MIO_OUTSEL_REGWEN::EN_0::CLEAR);
166    }
167
168    fn get_selector(self) -> PinmuxOutsel {
169        match PinmuxOutsel::try_from(PINMUX_BASE.mio_outsel[self as usize].get()) {
170            Ok(sel) => sel,
171            Err(val) => panic!("PINMUX: Invalid register value: {}", val),
174        }
175    }
176}
177
178pub trait SelectInput {
179    fn connect_input(self, input: PinmuxInsel);
181
182    fn connect_low(self);
184
185    fn connect_high(self);
187
188    fn lock(self);
190
191    fn get_selector(self) -> PinmuxInsel;
193}
194
195impl From<MuxedPads> for PinmuxInsel {
200    fn from(pad: MuxedPads) -> Self {
201        match PinmuxInsel::try_from(pad as u32 + PINMUX_MIO_PERIPH_INSEL_IDX_OFFSET as u32) {
203            Ok(select) => select,
204            Err(_) => PinmuxInsel::ConstantZero,
205        }
206    }
207}
208
209impl SelectInput for PinmuxPeripheralIn {
210    fn connect_input(self, input: PinmuxInsel) {
211        PINMUX_BASE.mio_periph_insel[self as usize].set(input as u32)
212    }
213
214    fn connect_low(self) {
215        PINMUX_BASE.mio_periph_insel[self as usize].set(PinmuxInsel::ConstantZero as u32)
216    }
217
218    fn connect_high(self) {
219        PINMUX_BASE.mio_periph_insel[self as usize].set(PinmuxInsel::ConstantOne as u32)
220    }
221
222    fn lock(self) {
223        PINMUX_BASE.mio_periph_insel_regwen[self as usize]
224            .write(MIO_PERIPH_INSEL_REGWEN::EN_0::CLEAR);
225    }
226
227    fn get_selector(self) -> PinmuxInsel {
228        match PinmuxInsel::try_from(PINMUX_BASE.mio_periph_insel[self as usize].get()) {
229            Ok(sel) => sel,
230            Err(val) => panic!("PINMUX: Invalid insel register value {}", val),
232        }
233    }
234}
235
236#[derive(Copy, Clone, PartialEq, Eq)]
246pub enum PadConfig {
247    Unconnected,
249    Input(MuxedPads, PinmuxPeripheralIn),
252    Output(MuxedPads, PinmuxOutsel),
255    InOut(MuxedPads, PinmuxPeripheralIn, PinmuxOutsel),
259}
260
261impl PadConfig {
262    pub fn connect(&self) {
264        match *self {
265            PadConfig::Unconnected => {}
266            PadConfig::Input(pad, peripheral_in) => {
267                peripheral_in.connect_input(PinmuxInsel::from(pad));
268            }
269            PadConfig::Output(pad, peripheral_out) => {
270                pad.connect_output(peripheral_out);
271            }
272            PadConfig::InOut(pad, peripheral_in, peripheral_out) => {
273                peripheral_in.connect_input(PinmuxInsel::from(pad));
274                pad.connect_output(peripheral_out);
275            }
276        }
277    }
278
279    pub fn disconnect_input(&self) {
281        match *self {
282            PadConfig::Unconnected => {}
283            PadConfig::Input(_pad, peripheral_in) => peripheral_in.connect_low(),
284            PadConfig::Output(_pad, _peripheral_out) => {}
285            PadConfig::InOut(_pad, peripheral_in, _peripheral_out) => {
286                peripheral_in.connect_low();
287            }
288        }
289    }
290
291    pub fn disconnect_output(&self) {
293        match *self {
294            PadConfig::Unconnected => {}
295            PadConfig::Input(_pad, _peripheral_in) => {}
296            PadConfig::Output(pad, _peripheral_out) => pad.connect_high_z(),
297            PadConfig::InOut(pad, _peripheral_in, _peripheral_out) => {
298                pad.connect_high_z();
299            }
300        }
301    }
302
303    pub fn disconnect(&self) {
306        match *self {
307            PadConfig::Unconnected => {}
308            PadConfig::Input(_pad, peripheral_in) => {
309                peripheral_in.connect_low();
310            }
311            PadConfig::Output(pad, _peripheral_out) => {
312                pad.connect_high_z();
313            }
314            PadConfig::InOut(pad, peripheral_in, _peripheral_out) => {
315                peripheral_in.connect_low();
316                pad.connect_high_z();
317            }
318        }
319    }
320
321    pub fn get_pad(&self) -> Option<Pad> {
324        match *self {
325            PadConfig::Unconnected => None,
326            PadConfig::Input(pad, _) => Some(Pad::Mio(pad)),
327            PadConfig::Output(pad, _) => Some(Pad::Mio(pad)),
328            PadConfig::InOut(pad, _, _) => Some(Pad::Mio(pad)),
329        }
330    }
331}
332
333impl From<PadConfig> for Configuration {
334    fn from(pad: PadConfig) -> Configuration {
335        match pad {
336            PadConfig::Unconnected => Configuration::Other,
337            PadConfig::Input(_pad, peripheral_in) => match peripheral_in.get_selector() {
338                PinmuxInsel::ConstantZero => Configuration::LowPower,
339                PinmuxInsel::ConstantOne => Configuration::Function,
340                _ => Configuration::Input,
341            },
342            PadConfig::Output(pad, _peripheral_out) => match pad.get_selector() {
343                PinmuxOutsel::ConstantZero => Configuration::Function,
344                PinmuxOutsel::ConstantOne => Configuration::Function,
345                PinmuxOutsel::ConstantHighZ => Configuration::LowPower,
346                _ => Configuration::Output,
347            },
348            PadConfig::InOut(pad, peripheral_in, _peripheral_out) => {
349                let input_selector = peripheral_in.get_selector();
350                let output_selector = pad.get_selector();
351                match (input_selector, output_selector) {
352                    (PinmuxInsel::ConstantZero, PinmuxOutsel::ConstantHighZ) => {
353                        Configuration::LowPower
354                    }
355                    (
356                        PinmuxInsel::ConstantOne | PinmuxInsel::ConstantZero,
357                        PinmuxOutsel::ConstantZero | PinmuxOutsel::ConstantOne,
358                    ) => Configuration::Function,
359                    (_, _) => Configuration::InputOutput,
360                }
361            }
362        }
363    }
364}
365
366impl Configure for PadConfig {
367    fn configuration(&self) -> Configuration {
368        Configuration::from(*self)
369    }
370
371    fn make_output(&self) -> Configuration {
372        if let Configuration::LowPower = self.configuration() {
373            self.connect()
374        }
375        self.configuration()
376    }
377
378    fn disable_output(&self) -> Configuration {
379        self.disconnect_output();
380        self.configuration()
381    }
382
383    fn make_input(&self) -> Configuration {
384        if let Configuration::LowPower = self.configuration() {
385            self.connect()
386        }
387        self.configuration()
388    }
389
390    fn disable_input(&self) -> Configuration {
391        self.disconnect_input();
392        self.configuration()
393    }
394
395    fn deactivate_to_low_power(&self) {
396        self.disconnect();
397    }
398
399    fn set_floating_state(&self, state: FloatingState) {
400        if let Some(pad) = self.get_pad() {
401            pad.set_floating_state(state);
402        }
403    }
404
405    fn floating_state(&self) -> FloatingState {
406        if let Some(pad) = self.get_pad() {
407            pad.floating_state()
408        } else {
409            FloatingState::PullNone
410        }
411    }
412}