sam4l/acifc.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Implementation of the SAM4L ACIFC controller.
6//!
7//! See datasheet section "37. Analog Comparator Interface (ACIFC)".
8//!
9//! Overview
10//! -----
11//! The SAM4L Analog Comparator Interface (ACIFC) controls a number of Analog
12//! Comparators (ACs) with identical behavior. Each Analog Comparator compares
13//! two voltages and gives an output depending on this comparison. A specific AC
14//! is referred to as ACx where x is any number from 0 to n and n is the index
15//! of last AC module.
16//!
17//! The number of analog comparators (ACs) available depends on the board pr
18//! microcontroller used. The SAM4Lmcomes in three different versions: a 48-pin, a
19//! 64-pin and a 100-pin version. On the 48-pin version, one AC is available.
20//! On the 64-pin version, two ACs are available. On the 100-pin version, four
21//! ACs are available.
22//! The Hail is an example of a board with the 64-pin version of the SAM4L,
23//! and therefore supports two ACs.
24//! The Imix is an example of a board with the 100-pin version of the SAM4L,
25//! and therefore supports four ACs.
26//! Currently, no version of the SAM4L exists with all the 8 ACs
27//! implemented. Therefore a lot of the defined bitfields remain unused, but
28//! are initialized for a possible future scenario.
29
30// Author: Danilo Verhaert <verhaert@cs.stanford.edu>
31
32use crate::pm;
33use core::cell::Cell;
34use kernel::ErrorCode;
35use kernel::debug;
36use kernel::hil::analog_comparator;
37use kernel::utilities::StaticRef;
38use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
39use kernel::utilities::registers::{
40 ReadOnly, ReadWrite, WriteOnly, register_bitfields, register_structs,
41};
42
43/// Representation of an AC channel on the SAM4L.
44pub struct AcChannel {
45 chan_num: u32,
46}
47
48#[derive(Copy, Clone, Debug)]
49#[repr(u8)]
50pub enum Channel {
51 AC0 = 0x00,
52 AC1 = 0x01,
53 AC2 = 0x02,
54 AC3 = 0x03,
55}
56
57/// Initialization of an AC channel.
58impl AcChannel {
59 /// Create a new AC channel.
60 ///
61 /// - `channel`: Channel enum representing the channel number
62 pub const fn new(channel: Channel) -> AcChannel {
63 AcChannel {
64 chan_num: ((channel as u8) & 0x0F) as u32,
65 }
66 }
67}
68
69register_structs! {
70 AcifcRegisters {
71 (0x000 => ctrl: ReadWrite<u32, Control::Register>),
72 (0x004 => sr: ReadOnly<u32, Status::Register>),
73 (0x008 => _reserved0: [ReadOnly<u32>; 2]),
74 (0x010 => ier: WriteOnly<u32, Interrupt::Register>),
75 (0x014 => idr: WriteOnly<u32, Interrupt::Register>),
76 (0x018 => imr: ReadOnly<u32, Interrupt::Register>),
77 (0x01C => isr: ReadOnly<u32, Interrupt::Register>),
78 (0x020 => icr: WriteOnly<u32, Interrupt::Register>),
79 (0x024 => tr: ReadWrite<u32, Test::Register>),
80 (0x028 => _reserved1: [ReadOnly<u32>; 2]),
81 (0x030 => parameter: ReadOnly<u32, Parameter::Register>),
82 (0x034 => version: ReadOnly<u32>),
83 (0x038 => _reserved2: [ReadOnly<u32>; 18]),
84 (0x080 => confw: [ReadWrite<u32, WindowConfiguration::Register>; 4]),
85 (0x090 => _reserved3: [ReadOnly<u32>; 16]),
86 (0x0D0 => conf: [ReadWrite<u32, ACConfiguration::Register>; 8]),
87 (0x0F0 => @END),
88 }
89}
90
91register_bitfields![u32,
92 Control [
93 /// Analog comparator test mode. Equal to 1 means AC outputs will be
94 /// bypassed with values in AC test register.
95 ACTEST 7,
96 /// This bit is set when an enabled peripheral event is received (called
97 /// by EVENTEN), and starts a single comparison.
98 ESTART 5,
99 /// This bit can be set by the user and starts a single comparison.
100 USTART 4,
101 /// This bit sets ESTART to 1 on receiving a peripheral event from
102 /// another hardware module.
103 EVENTEN 1,
104 /// Enables or disables the ACIFC.
105 EN 0
106 ],
107
108 Status [
109 /// This bit represents an output for the window mode, and reads one
110 /// when the common input voltage is inside the window of the two
111 /// non-common inputs.
112 WFCS3 27,
113 WFCS2 26,
114 WFCS1 25,
115 WFCS0 24,
116 /// ACRDY is set when the AC output is ready. ACCS is set when the
117 /// positive input voltage V_{INP} is greater than the negative input
118 /// voltage V_{INN}.
119 ACRDY7 15,
120 ACCS7 14,
121 ACRDY6 13,
122 ACCS6 12,
123 ACRDY5 11,
124 ACCS5 10,
125 ACRDY4 9,
126 ACCS4 8,
127 ACRDY3 7,
128 ACCS3 6,
129 ACRDY2 5,
130 ACCS2 4,
131 ACRDY1 3,
132 ACCS1 2,
133 ACRDY0 1,
134 ACCS0 0
135 ],
136
137 /// - IER: Writing a one to a bit in this register will set the
138 /// corresponding bit in IMR.
139 /// - IDR: Writing a one to a bit in this register will clear the
140 /// corresponding bit in IMR.
141 /// - IMR: Writing a one in any of these bits will enable the corresponding
142 /// interrupt.
143 /// - ISR: WFINTx shows if a window mode interrupt is pending. SUTINTx shows
144 /// if a startup time interrupt is pending. ACINTx shows if a normal mode
145 /// interrupt is pending.
146 /// - ICR: Writing a one to a bit in this register will clear the
147 /// corresponding bit in ISR and the corresponding interrupt request.
148 Interrupt [
149 WFINT3 27,
150 WFINT2 26,
151 WFINT1 25,
152 WFINT0 24,
153 SUTINT7 15,
154 ACINT7 14,
155 SUTINT6 13,
156 ACINT6 12,
157 SUTINT5 11,
158 ACINT5 10,
159 SUTINT4 9,
160 ACINT4 8,
161 SUTINT3 7,
162 ACINT3 6,
163 SUTINT2 5,
164 ACINT2 4,
165 SUTINT1 3,
166 ACINT1 2,
167 SUTINT0 1,
168 ACINT0 0
169 ],
170
171 Test [
172 /// If equal to one, overrides ACx output with the value of ACTESTx.
173 ACTEST7 7,
174 ACTEST6 6,
175 ACTEST5 5,
176 ACTEST4 4,
177 ACTEST3 3,
178 ACTEST2 2,
179 ACTEST1 1,
180 ACTEST0 0
181 ],
182
183 Parameter [
184 /// If equal to one, window mode x is implemented.
185 WIMPL3 19,
186 WIMPL2 18,
187 WIMPL1 17,
188 WIMPL0 16,
189 /// If equal to one, analog comparator x is implemented.
190 ACIMPL7 7,
191 ACIMPL6 6,
192 ACIMPL5 5,
193 ACIMPL4 4,
194 ACIMPL3 3,
195 ACIMPL2 2,
196 ACIMPL1 1,
197 ACIMPL0 0
198 ],
199
200 WindowConfiguration [
201 /// If equal to one, window mode is enabled.
202 WFEN OFFSET(16) NUMBITS(1) [],
203 /// If equal to one, peripheral event from ACWOUT is enabled.
204 WEVEN OFFSET(11) NUMBITS(1) [],
205 /// Peripheral Event Source Selection for Window Mode
206 WEVSRC OFFSET (8) NUMBITS(3) [
207 AcwoutRisingEdge = 0,
208 AcwoutFallingEdge = 1,
209 AcwoutRisingOrFallingEdge = 2,
210 InsideWindow = 3,
211 OutsideWindow = 4,
212 MeasureDone = 5
213 ],
214 /// Window Mode Interrupt Settings
215 WIS OFFSET(0) NUMBITS (3)[
216 /// Window interrupt as soon as the common input voltage is inside
217 /// the window
218 InterruptInsideWindow = 0,
219 /// Window interrupt as soon as the common input voltage is outside
220 /// the window
221 InterruptOutsideWindow = 1,
222 /// Window interrupt on toggle of ACWOUT
223 InterruptToggleAcwout = 2,
224 /// Window interrupt when evaluation of common input voltage is done
225 InterruptAfterEvaluation = 3,
226 /// Window interrupt when the common input voltage enters the window
227 /// (i.e., rising-edge of ACWOUT)
228 InterruptEnterWindow = 4,
229 /// Window interrupt when the common input voltage leaves the window
230 /// (i.e., falling-edge of ACWOUT)
231 InterruptLeaveWindow = 5
232 ]
233 ],
234
235 ACConfiguration [
236 /// If equal to one, AC is always enabled.
237 ALWAYSON OFFSET(27) NUMBITS(1) [],
238 /// 0: Low-power mode. 1: Fastm ode.
239 FAST OFFSET(26) NUMBITS(1) [],
240 /// Hysteresis voltage value: 0/25/50/75 mV
241 HYS OFFSET(24) NUMBITS(2) [
242 HysteresisVoltage0mV = 0,
243 HysteresisVoltage25mV = 1,
244 HysteresisVoltage50mV = 2,
245 HysteresisVoltage75mV = 3
246 ],
247 /// Setting this to one will output peripheral event when ACOUT is zero.
248 EVENP OFFSET(17) NUMBITS(1) [],
249 /// Setting this to one will output peripheral event when ACOUT is one.
250 EVENN OFFSET(16) NUMBITS(1) [],
251 /// Negative input select. 00: ACANx pint selected, others reserved.
252 INSELN OFFSET(8) NUMBITS(2) [],
253 /// Choose between analog comparator mode.
254 MODE OFFSET(4) NUMBITS(2) [
255 Off = 0,
256 ContinuousMeasurementMode = 1,
257 /// User Triggered Single Measurement Mode
258 UserMode = 2,
259 /// Peripheral Event Single Measurement Mode
260 PeripheralMode = 3
261 ],
262 /// Interrupt settings
263 IS OFFSET(0) NUMBITS(2) [
264 /// When Vinp > Vinn
265 WhenVinpGtVinn = 0,
266 /// When Vinp < Vinn
267 WhenVinpLtVinn = 1,
268 /// On toggle of ACOUT
269 OnToggleOfACOUT = 2,
270 /// When comparison of Vinp and Vinn is done
271 WhenComparisonDone = 3
272 ]
273 ]
274];
275
276const ACIFC_BASE: StaticRef<AcifcRegisters> =
277 unsafe { StaticRef::new(0x40040000 as *const AcifcRegisters) };
278
279pub struct Acifc<'a> {
280 client: Cell<Option<&'a dyn analog_comparator::Client>>,
281}
282
283/// Implement constructor for struct Acifc
284impl<'a> Acifc<'a> {
285 pub const fn new() -> Acifc<'a> {
286 Acifc {
287 client: Cell::new(None),
288 }
289 }
290
291 fn enable_clock(&self) {
292 pm::enable_clock(pm::Clock::PBA(pm::PBAClock::ACIFC));
293 }
294
295 fn disable_clock(&self) {
296 pm::disable_clock(pm::Clock::PBA(pm::PBAClock::ACIFC));
297 }
298
299 /// Enabling the ACIFC by activating the clock and the ACs (Analog
300 /// Comparators). Currently always-on mode is enabled, allowing a
301 /// measurement on an AC to be made quickly after a measurement is
302 /// triggered, without waiting for the AC startup time. The drawback is
303 /// that the AC is always on, leading to a higher power dissipation.
304 fn enable(&self) {
305 let regs = ACIFC_BASE;
306 self.enable_clock();
307 regs.ctrl.write(Control::EN::SET);
308
309 // Enable continuous measurement mode and always-on mode for all the analog comparators
310 regs.conf[0].write(
311 ACConfiguration::MODE::ContinuousMeasurementMode + ACConfiguration::ALWAYSON::SET,
312 );
313 regs.conf[1].write(
314 ACConfiguration::MODE::ContinuousMeasurementMode + ACConfiguration::ALWAYSON::SET,
315 );
316 regs.conf[2].write(
317 ACConfiguration::MODE::ContinuousMeasurementMode + ACConfiguration::ALWAYSON::SET,
318 );
319 regs.conf[3].write(
320 ACConfiguration::MODE::ContinuousMeasurementMode + ACConfiguration::ALWAYSON::SET,
321 );
322
323 // Make sure enabling was succesful
324 let result = regs.ctrl.is_set(Control::EN);
325 if !result {
326 debug!("Failed enabling analog comparator, are you sure the clock is enabled?");
327 }
328 }
329
330 /// Disable the entire ACIFC
331 fn disable(&self) {
332 let regs = ACIFC_BASE;
333 self.disable_clock();
334 regs.ctrl.write(Control::EN::CLEAR);
335 }
336
337 /// Handling of interrupts. Currently set up so that an interrupt fires
338 /// only once when the condition is true (e.g. Vinp > Vinn), and then
339 /// doesn't fire anymore until the condition is false (e.g. Vinp < Vinn).
340 /// This way we won't get a barrage of interrupts as soon as Vinp > Vinn:
341 /// we'll get just one.
342 pub fn handle_interrupt(&self) {
343 let regs = ACIFC_BASE;
344
345 // We check which AC generated the interrupt, and callback to the client accordingly
346 if regs.isr.is_set(Interrupt::ACINT0) {
347 // Return if we had a pending interrupt while we already set IMR to 0 (edge case)
348 if !regs.imr.is_set(Interrupt::ACINT0) {
349 return;
350 }
351
352 // Disable IMR, making sure no more interrupts can occur until we write
353 // to IER
354 regs.idr.write(Interrupt::ACINT0::SET);
355
356 // If Vinp > Vinn, throw an interrupt to the client and set the AC so
357 // that it will throw an interrupt when Vinn < Vinp instead.
358 if !regs.conf[0].is_set(ACConfiguration::IS) {
359 self.client.get().map(|client| {
360 client.fired(0);
361 });
362 regs.conf[0].modify(ACConfiguration::IS::WhenVinpLtVinn);
363 }
364 // If Vinp < Vinn, set the AC so that it will throw an interrupt when
365 // Vinp > Vinn instead.
366 else {
367 regs.conf[0].modify(ACConfiguration::IS::WhenVinpGtVinn);
368 }
369
370 // Clear the interrupt request
371 regs.icr.write(Interrupt::ACINT0::SET);
372 regs.ier.write(Interrupt::ACINT0::SET);
373 } else if regs.isr.is_set(Interrupt::ACINT1) {
374 // Return if we had a pending interrupt while we already set IMR to 0 (edge case)
375 if !regs.imr.is_set(Interrupt::ACINT1) {
376 return;
377 }
378
379 // Disable IMR, making sure no more interrupts can occur until we write
380 // to IER
381 regs.idr.write(Interrupt::ACINT1::SET);
382
383 // If Vinp > Vinn, throw an interrupt to the client and set the AC so
384 // that it will throw an interrupt when Vinn < Vinp instead.
385 if !regs.conf[1].is_set(ACConfiguration::IS) {
386 self.client.get().map(|client| {
387 client.fired(1);
388 });
389 regs.conf[1].modify(ACConfiguration::IS::WhenVinpLtVinn);
390 }
391 // If Vinp < Vinn, set the AC so that it will throw an interrupt when
392 // Vinp > Vinn instead.
393 else {
394 regs.conf[1].modify(ACConfiguration::IS::WhenVinpGtVinn);
395 }
396
397 // Clear the interrupt request
398 regs.icr.write(Interrupt::ACINT1::SET);
399 regs.ier.write(Interrupt::ACINT1::SET);
400 } else if regs.isr.is_set(Interrupt::ACINT2) {
401 // Return if we had a pending interrupt while we already set IMR to 0 (edge case)
402 if !regs.imr.is_set(Interrupt::ACINT2) {
403 return;
404 }
405
406 // Disable IMR, making sure no more interrupts can occur until we write
407 // to IER
408 regs.idr.write(Interrupt::ACINT2::SET);
409
410 // If Vinp > Vinn, throw an interrupt to the client and set the AC so
411 // that it will throw an interrupt when Vinn < Vinp instead.
412 if !regs.conf[2].is_set(ACConfiguration::IS) {
413 self.client.get().map(|client| {
414 client.fired(2);
415 });
416 regs.conf[2].modify(ACConfiguration::IS::WhenVinpLtVinn);
417 }
418 // If Vinp < Vinn, set the AC so that it will throw an interrupt when
419 // Vinp > Vinn instead.
420 else {
421 regs.conf[2].modify(ACConfiguration::IS::WhenVinpGtVinn);
422 }
423
424 // Clear the interrupt request
425 regs.icr.write(Interrupt::ACINT2::SET);
426 regs.ier.write(Interrupt::ACINT2::SET);
427 } else if regs.isr.is_set(Interrupt::ACINT3) {
428 // Return if we had a pending interrupt while we already set IMR to 0 (edge case)
429 if !regs.imr.is_set(Interrupt::ACINT3) {
430 return;
431 }
432
433 // Disable IMR, making sure no more interrupts can occur until we write
434 // to IER
435 regs.idr.write(Interrupt::ACINT3::SET);
436
437 // If Vinp > Vinn, throw an interrupt to the client and set the AC so
438 // that it will throw an interrupt when Vinn < Vinp instead.
439 if !regs.conf[3].is_set(ACConfiguration::IS) {
440 self.client.get().map(|client| {
441 client.fired(3);
442 });
443 regs.conf[3].modify(ACConfiguration::IS::WhenVinpLtVinn);
444 }
445 // If Vinp < Vinn, set the AC so that it will throw an interrupt when
446 // Vinp > Vinn instead.
447 else {
448 regs.conf[3].modify(ACConfiguration::IS::WhenVinpGtVinn);
449 }
450
451 // Clear the interrupt request
452 regs.icr.write(Interrupt::ACINT3::SET);
453 regs.ier.write(Interrupt::ACINT3::SET);
454 }
455 }
456}
457
458impl<'a> analog_comparator::AnalogComparator<'a> for Acifc<'a> {
459 type Channel = AcChannel;
460
461 /// Do a single comparison
462 fn comparison(&self, channel: &Self::Channel) -> bool {
463 self.enable();
464 let regs = ACIFC_BASE;
465 let result = if channel.chan_num == 0 {
466 regs.sr.is_set(Status::ACCS0)
467 } else if channel.chan_num == 1 {
468 regs.sr.is_set(Status::ACCS1)
469 } else if channel.chan_num == 2 {
470 regs.sr.is_set(Status::ACCS2)
471 } else if channel.chan_num == 3 {
472 regs.sr.is_set(Status::ACCS3)
473 } else {
474 // Should never get here, just making sure
475 self.disable();
476 panic!("PANIC! Please choose a comparator (value of ac) that this chip supports");
477 };
478 result
479 }
480
481 /// Start interrupt-based comparisons
482 fn start_comparing(&self, channel: &Self::Channel) -> Result<(), ErrorCode> {
483 self.enable();
484 let regs = ACIFC_BASE;
485
486 if channel.chan_num == 0 {
487 // Enable interrupts.
488 regs.ier.write(Interrupt::ACINT0::SET);
489 Ok(())
490 } else if channel.chan_num == 1 {
491 // Repeat the same for ac == 1
492 regs.ier.write(Interrupt::ACINT1::SET);
493 Ok(())
494 } else if channel.chan_num == 2 {
495 // Repeat the same for ac == 2
496 regs.ier.write(Interrupt::ACINT2::SET);
497 Ok(())
498 } else if channel.chan_num == 3 {
499 // Repeat the same for ac == 3
500 regs.ier.write(Interrupt::ACINT3::SET);
501 Ok(())
502 } else {
503 // Should never get here, just making sure
504 self.disable();
505 debug!("Please choose a comparator (value of ac) that this chip supports");
506 Err(ErrorCode::INVAL)
507 }
508 }
509
510 /// Stop interrupt-based comparisons
511 fn stop_comparing(&self, channel: &Self::Channel) -> Result<(), ErrorCode> {
512 let regs = ACIFC_BASE;
513
514 if channel.chan_num == 0 {
515 // Disable interrupts.
516 regs.ier.write(Interrupt::ACINT0::CLEAR);
517 Ok(())
518 } else if channel.chan_num == 1 {
519 // Repeat the same for ac == 1
520 regs.ier.write(Interrupt::ACINT1::CLEAR);
521 Ok(())
522 } else if channel.chan_num == 2 {
523 // Repeat the same for ac == 2
524 regs.ier.write(Interrupt::ACINT2::CLEAR);
525 Ok(())
526 } else if channel.chan_num == 3 {
527 // Repeat the same for ac == 3
528 regs.ier.write(Interrupt::ACINT3::CLEAR);
529 Ok(())
530 } else {
531 // Should never get here, just making sure
532 self.disable();
533 debug!("Please choose a comparator (value of ac) that this chip supports");
534 Err(ErrorCode::INVAL)
535 }
536 }
537
538 fn set_client(&self, client: &'a dyn analog_comparator::Client) {
539 self.client.set(Some(client));
540 }
541}