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}