atsamd_hal/gpio/reg.rs
1use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
2
3#[hal_cfg(any("port-d11", "port-d21"))]
4use crate::pac::port::{
5 Ctrl, Dir, Dirclr, Dirset, Dirtgl, In, Out, Outclr, Outset, Outtgl, Pincfg0_ as Pincfg,
6 Pmux0_ as Pmux, Wrconfig,
7};
8
9#[hal_cfg("port-d5x")]
10use crate::pac::port::group::{
11 Ctrl, Dir, Dirclr, Dirset, Dirtgl, In, Out, Outclr, Outset, Outtgl, Pincfg, Pmux, Wrconfig,
12};
13
14use crate::pac::Port;
15
16use super::dynpin::*;
17
18//==============================================================================
19// ModeFields
20//==============================================================================
21
22/// Collect all fields needed to set the [`PinMode`](super::PinMode)
23#[derive(Default)]
24struct ModeFields {
25 dir: bool,
26 inen: bool,
27 pullen: bool,
28 out: bool,
29 pmuxen: bool,
30 pmux: u8,
31}
32
33impl From<DynPinMode> for ModeFields {
34 #[inline]
35 #[hal_macro_helper]
36 fn from(mode: DynPinMode) -> Self {
37 let mut fields = Self::default();
38 use DynPinMode::*;
39 match mode {
40 Disabled(config) => {
41 use DynDisabled::*;
42 match config {
43 Floating => {
44 fields.pullen = false;
45 fields.out = false;
46 }
47 PullDown => {
48 fields.pullen = true;
49 fields.out = false;
50 }
51 PullUp => {
52 fields.pullen = true;
53 fields.out = true;
54 }
55 }
56 }
57 Input(config) => {
58 fields.inen = true;
59 use DynInput::*;
60 match config {
61 Floating => {
62 fields.pullen = false;
63 fields.out = false;
64 }
65 PullDown => {
66 fields.pullen = true;
67 fields.out = false;
68 }
69 PullUp => {
70 fields.pullen = true;
71 fields.out = true;
72 }
73 }
74 }
75 Interrupt(config) => {
76 fields.pmuxen = true;
77 fields.pmux = 0;
78 use DynInterrupt::*;
79 match config {
80 Floating => {
81 fields.pullen = false;
82 fields.out = false;
83 }
84 PullDown => {
85 fields.pullen = true;
86 fields.out = false;
87 }
88 PullUp => {
89 fields.pullen = true;
90 fields.out = true;
91 }
92 }
93 }
94 Output(config) => {
95 fields.dir = true;
96 use DynOutput::*;
97 match config {
98 PushPull => {
99 fields.inen = false;
100 }
101 Readable => {
102 fields.inen = true;
103 }
104 }
105 }
106 Alternate(config) => {
107 fields.pmuxen = true;
108 use DynAlternate::*;
109 match config {
110 B => {
111 fields.pmux = 1;
112 }
113 C => {
114 fields.pmux = 2;
115 }
116 D => {
117 fields.pmux = 3;
118 }
119 E => {
120 fields.pmux = 4;
121 }
122 F => {
123 fields.pmux = 5;
124 }
125 G => {
126 fields.pmux = 6;
127 }
128 #[hal_cfg(any("port-d21", "port-d5x"))]
129 H => {
130 fields.pmux = 7;
131 }
132 #[hal_cfg("port-d5x")]
133 I => {
134 fields.pmux = 8;
135 }
136 #[hal_cfg("port-d5x")]
137 J => {
138 fields.pmux = 9;
139 }
140 #[hal_cfg("port-d5x")]
141 K => {
142 fields.pmux = 10;
143 }
144 #[hal_cfg("port-d5x")]
145 L => {
146 fields.pmux = 11;
147 }
148 #[hal_cfg("port-d5x")]
149 M => {
150 fields.pmux = 12;
151 }
152 #[hal_cfg("port-d5x")]
153 N => {
154 fields.pmux = 13;
155 }
156 }
157 }
158 };
159 fields
160 }
161}
162
163//==============================================================================
164// GROUP
165//==============================================================================
166
167/// Represent the [`Port`] register block
168///
169/// The SAMx5x PACs have a GROUP type to represent each [`Port`] group, but the
170/// SAMD11 and SAMD21 PACs do not. Manually re-implement it here.
171#[repr(C)]
172#[allow(clippy::upper_case_acronyms)]
173pub(super) struct GROUP {
174 dir: Dir,
175 dirclr: Dirclr,
176 dirset: Dirset,
177 dirtgl: Dirtgl,
178 out: Out,
179 outclr: Outclr,
180 outset: Outset,
181 outtgl: Outtgl,
182 in_: In,
183 ctrl: Ctrl,
184 wrconfig: Wrconfig,
185 _padding1: [u8; 4],
186 pmux: [Pmux; 16],
187 pincfg: [Pincfg; 32],
188 _padding2: [u8; 32],
189}
190
191//==============================================================================
192// RegisterInterface
193//==============================================================================
194
195/// Provide a safe register interface for pin objects
196///
197/// [`Port`], like every PAC `struct`, is [`Send`] but not [`Sync`], because it
198/// points to a `RegisterBlock` of `VolatileCell`s. Unfortunately, such an
199/// interface is quite restrictive. Instead, it would be ideal if we could split
200/// the [`Port`] into independent pins that are both [`Send`] and [`Sync`].
201///
202/// [`Port`] is a single, zero-sized marker `struct` that provides access to
203/// every [`Port`] register. Instead, we would like to create zero-sized marker
204/// `struct`s for every pin, where each pin is only allowed to control its own
205/// registers. Furthermore, each pin `struct` should be a singleton, so that
206/// exclusive access to the `struct` also guarantees exclusive access to the
207/// corresponding registers. Finally, the pin `struct`s should not have any
208/// interior mutability. Together, these requirements would allow the pin
209/// `struct`s to be both [`Send`] and [`Sync`].
210///
211/// This trait creates a safe API for accomplishing these goals. Implementers
212/// supply a pin ID through the [`id`] function. The remaining functions provide
213/// a safe API for accessing the registers associated with that pin ID. Any
214/// modification of the registers requires `&mut self`, which destroys interior
215/// mutability.
216///
217/// # Safety
218///
219/// Users should only implement the [`id`] function. No default function
220/// implementations should be overridden. The implementing type must also have
221/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
222/// pin ID is a singleton.
223///
224/// [`id`]: Self::id
225pub(super) unsafe trait RegisterInterface {
226 /// Provide a [`DynPinId`] identifying the set of registers controlled by
227 /// this type.
228 fn id(&self) -> DynPinId;
229
230 /// Pointer to the array of [`GROUP`] register blocks
231 const GROUPS: *const GROUP = Port::ptr() as *const _;
232
233 #[inline]
234 #[hal_macro_helper]
235 fn group(&self) -> &GROUP {
236 let offset = match self.id().group {
237 DynGroup::A => 0,
238 #[hal_cfg("pin-group-b")]
239 DynGroup::B => 1,
240 #[hal_cfg("pin-group-c")]
241 DynGroup::C => 2,
242 #[hal_cfg("pin-group-d")]
243 DynGroup::D => 3,
244 };
245 // Safety: It is safe to create shared references to each PAC register
246 // or register block, because all registers are wrapped in
247 // `UnsafeCell`s. We should never create unique references to the
248 // registers, to prevent any risk of UB.
249 unsafe { &*Self::GROUPS.add(offset) }
250 }
251
252 #[inline]
253 fn mask_32(&self) -> u32 {
254 1 << self.id().num
255 }
256
257 #[inline]
258 fn mask_16(&self) -> u16 {
259 1 << (self.id().num & 0xF)
260 }
261
262 #[inline]
263 fn hwsel(&self) -> bool {
264 self.id().num & 0x10 != 0
265 }
266
267 #[inline]
268 fn pincfg(&self) -> &Pincfg {
269 &self.group().pincfg[self.id().num as usize]
270 }
271
272 /// Change the pin mode
273 ///
274 /// We use the Wrconfig register to avoid using the Pmux register. Each Pmux
275 /// register stores the Pmux values for two different pins. Changing the
276 /// Pmux value for one pin would require a read/modify/write operation that
277 /// could be preempted by the other pin. This is fundamentally unsound. The
278 /// Wrconfig register lets us modify *only* the fields corresponding to this
279 /// particular PinId/DynPinId.
280 #[inline]
281 fn change_mode(&mut self, mode: DynPinMode) {
282 let ModeFields {
283 dir,
284 inen,
285 pullen,
286 out,
287 pmuxen,
288 pmux,
289 } = mode.into();
290 // The bit patterns here are guaranteed to be safe, because they can
291 // ultimately be traced back to associated constants defined on the
292 // `PinId` and `PinMode` traits, which are guaranteed to be correct.
293 self.group().wrconfig.write(|w| unsafe {
294 w.hwsel().bit(self.hwsel());
295 w.wrpincfg().set_bit();
296 w.wrpmux().set_bit();
297 w.pmux().bits(pmux);
298 w.pullen().bit(pullen);
299 w.inen().bit(inen);
300 w.pmuxen().bit(pmuxen);
301 w.pinmask().bits(self.mask_16())
302 });
303 self.set_dir(dir);
304 if pullen {
305 self.write_pin(out)
306 };
307 }
308
309 /// Set the direction of a pin
310 #[inline]
311 fn set_dir(&mut self, bit: bool) {
312 let mask = self.mask_32();
313 // Safety: Dirset & Dirclr are "mask" registers, and we only write the
314 // bit for this pin ID
315 unsafe {
316 if bit {
317 self.group().dirset.write(|w| w.bits(mask));
318 } else {
319 self.group().dirclr.write(|w| w.bits(mask));
320 }
321 }
322 }
323
324 /// Read the logic level of an input put
325 #[inline]
326 #[allow(dead_code)]
327 fn read_pin(&self) -> bool {
328 let mask = self.mask_32();
329 self.group().in_.read().bits() & mask != 0
330 }
331
332 /// Write the logic level of an output pin
333 #[inline]
334 fn write_pin(&mut self, bit: bool) {
335 let mask = self.mask_32();
336 // Safety: Outset & Outclr are "mask" registers, and we only write the
337 // bit for this pin ID
338 unsafe {
339 if bit {
340 self.group().outset.write(|w| w.bits(mask));
341 } else {
342 self.group().outclr.write(|w| w.bits(mask));
343 }
344 }
345 }
346
347 /// Toggle the logic level of an output pin
348 #[inline]
349 fn toggle_pin(&mut self) {
350 let mask = self.mask_32();
351 // Safety: Outtgl is a "mask" register, and we only write the bit for
352 // this pin ID
353 unsafe { self.group().outtgl.write(|w| w.bits(mask)) };
354 }
355
356 /// Read back the logic level of an output pin
357 #[inline]
358 #[allow(dead_code)]
359 fn read_out_pin(&self) -> bool {
360 let mask = self.mask_32();
361 self.group().out.read().bits() & mask != 0
362 }
363
364 /// Read the drive strength of a pin
365 #[inline]
366 fn read_drive_strength(&self) -> bool {
367 self.pincfg().read().drvstr().bit()
368 }
369
370 /// Write the drive strength of a pin
371 #[inline]
372 fn write_drive_strength(&mut self, bit: bool) {
373 self.pincfg().modify(|_, w| w.drvstr().bit(bit));
374 }
375}