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