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 match self.configuration() {
373 Configuration::LowPower => self.connect(),
374 _ => {}
375 }
376 self.configuration()
377 }
378
379 fn disable_output(&self) -> Configuration {
380 self.disconnect_output();
381 self.configuration()
382 }
383
384 fn make_input(&self) -> Configuration {
385 match self.configuration() {
386 Configuration::LowPower => self.connect(),
387 _ => {}
388 }
389 self.configuration()
390 }
391
392 fn disable_input(&self) -> Configuration {
393 self.disconnect_input();
394 self.configuration()
395 }
396
397 fn deactivate_to_low_power(&self) {
398 self.disconnect();
399 }
400
401 fn set_floating_state(&self, state: FloatingState) {
402 if let Some(pad) = self.get_pad() {
403 pad.set_floating_state(state);
404 }
405 }
406
407 fn floating_state(&self) -> FloatingState {
408 if let Some(pad) = self.get_pad() {
409 pad.floating_state()
410 } else {
411 FloatingState::PullNone
412 }
413 }
414}