sam4l/
dac.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 DACC.
6//!
7//! Ensure that the `ADVREFP` pin is tied to `ADDANA`.
8//!
9//! - Author: Justin Hsieh <hsiehju@umich.edu>
10//! - Date: May 26th, 2017
11
12use crate::pm::{self, Clock, PBAClock};
13use core::cell::Cell;
14use kernel::hil;
15use kernel::utilities::registers::interfaces::{Readable, Writeable};
16use kernel::utilities::registers::{register_bitfields, ReadOnly, ReadWrite, WriteOnly};
17use kernel::utilities::StaticRef;
18use kernel::ErrorCode;
19
20#[repr(C)]
21pub struct DacRegisters {
22    // From page 905 of SAM4L manual
23    cr: WriteOnly<u32, Control::Register>, //             Control                       (0x00)
24    mr: ReadWrite<u32, Mode::Register>,    //                Mode                          (0x04)
25    cdr: WriteOnly<u32, ConversionData::Register>, //     Conversion Data Register      (0x08)
26    ier: WriteOnly<u32, InterruptEnable::Register>, //    Interrupt Enable Register     (0x0c)
27    idr: WriteOnly<u32, InterruptDisable::Register>, //   Interrupt Disable Register    (0x10)
28    imr: ReadOnly<u32, InterruptMask::Register>, //       Interrupt Mask Register       (0x14)
29    isr: ReadOnly<u32, InterruptStatus::Register>, //     Interrupt Status Register     (0x18)
30    _reserved0: [u32; 50], //                                                               (0x1c - 0xe0)
31    wpmr: ReadWrite<u32, WriteProtectMode::Register>, //  Write Protect Mode Register   (0xe4)
32    wpsr: ReadOnly<u32, WriteProtectStatus::Register>, // Write Protect Status Register (0xe8)
33    _reserved1: [u32; 4], //                                                                (0xec - 0xf8)
34    version: ReadOnly<u32, Version::Register>, //         Version Register              (0xfc)
35}
36
37register_bitfields![u32,
38    Control [
39        SWRST 0
40    ],
41
42    /// Mode of the DAC peripheral.
43    Mode [
44        /// Clock divider for internal trigger
45        CLKDIV   OFFSET(16)  NUMBITS(16) [],
46        /// Startup time selection
47        STARTUP  OFFSET( 8)  NUMBITS(8) [],
48        /// Word transfer
49        WORD     OFFSET( 5)  NUMBITS(1) [
50            HalfWordTransfer = 0b0,
51            FullWordTransfer = 0b1
52        ],
53        /// DAC enable
54        DACEN    OFFSET( 4)  NUMBITS(1) [],
55        /// Trigger selection
56        TRGSEL   OFFSET( 1)  NUMBITS(3) [
57            ExternalTrigger = 0b000,
58            PeripheralTrigger = 0b001
59        ],
60        /// Trigger enable
61        TRGEN    OFFSET( 0)  NUMBITS(1) [
62            InternalTrigger = 0b0,
63            ExternalTrigger = 0b1
64        ]
65    ],
66
67    /// Conversion Data Register
68    ConversionData [
69        /// Data to convert
70        DATA OFFSET(0) NUMBITS(32) []
71    ],
72
73    /// Interupt Enable Register
74    InterruptEnable [
75        /// TX ready
76        TXRDY 0
77    ],
78
79    /// Interrupt Disable Register
80    InterruptDisable [
81        /// TX ready
82        TXRDY 0
83    ],
84
85    /// Interrupt Mask Register
86    InterruptMask [
87        /// TX ready
88        TXRDY 0
89    ],
90
91    /// Interrupt Status Register
92    InterruptStatus [
93        /// TX ready
94        TXRDY 0
95    ],
96
97    /// Write Protect Mode Register
98    WriteProtectMode [
99        /// Write protect key
100        WPKEY OFFSET(8) NUMBITS(24) [],
101        /// Write protect enable
102        WPEN OFFSET(0) NUMBITS(1) []
103    ],
104
105    /// Write Protect Status Register
106    WriteProtectStatus [
107        /// Write protection error address
108        WPROTADDR OFFSET(8) NUMBITS(8) [],
109        /// Write protection error
110        WPROTERR OFFSET(0) NUMBITS(1) []
111    ],
112
113    /// Version Register
114    Version [
115        VARIANT OFFSET(16) NUMBITS(3) [],
116        VERSION OFFSET( 0) NUMBITS(12) []
117    ]
118];
119
120// Page 59 of SAM4L data sheet
121const DAC_BASE: StaticRef<DacRegisters> =
122    unsafe { StaticRef::new(0x4003C000 as *const DacRegisters) };
123
124pub struct Dac {
125    registers: StaticRef<DacRegisters>,
126    enabled: Cell<bool>,
127}
128
129impl Dac {
130    pub const fn new() -> Self {
131        Self {
132            registers: DAC_BASE,
133            enabled: Cell::new(false),
134        }
135    }
136
137    fn initialize(&self) -> Result<(), ErrorCode> {
138        if !self.enabled.get() {
139            self.enabled.set(true);
140
141            // Start the APB clock (CLK_DACC)
142            pm::enable_clock(Clock::PBA(PBAClock::DACC));
143
144            // Reset DACC
145            self.registers.cr.write(Control::SWRST::SET);
146
147            // Set Mode Register
148            // -half-word transfer mode
149            // -start up time max (0xFF)
150            // -clock divider from 48 MHz to 500 kHz (0x60)
151            // -internal trigger
152            // -enable dacc
153            let mr = Mode::WORD::HalfWordTransfer
154                + Mode::STARTUP.val(0xff)
155                + Mode::CLKDIV.val(0x60)
156                + Mode::TRGEN::InternalTrigger
157                + Mode::DACEN::SET;
158            self.registers.mr.write(mr);
159        }
160        Ok(())
161    }
162
163    // Not currently using interrupt.
164    pub fn handle_interrupt(&self) {}
165}
166
167impl hil::dac::DacChannel for Dac {
168    fn set_value(&self, value: usize) -> Result<(), ErrorCode> {
169        if !self.enabled.get() {
170            self.initialize()?;
171        }
172
173        // Check if ready to write to CDR
174        if !self.registers.isr.is_set(InterruptStatus::TXRDY) {
175            return Err(ErrorCode::BUSY);
176        }
177
178        // Write to CDR
179        self.registers
180            .cdr
181            .write(ConversionData::DATA.val(value as u32));
182        Ok(())
183    }
184}