msp432/
wdt.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//! Watchdog Timer (WDT)
6
7use kernel::utilities::registers::interfaces::{ReadWriteable, Readable};
8use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
9use kernel::utilities::StaticRef;
10
11const WATCHDOG_BASE: StaticRef<WdtRegisters> =
12    unsafe { StaticRef::new(0x4000_4800u32 as *const WdtRegisters) };
13
14// Every write access has to set this 'password' in the upper 8 bit of the
15// register, otherwise the watchdog resets the whole system
16const PASSWORD: u16 = 0x5A;
17
18register_structs! {
19    /// WDT_A
20    WdtRegisters {
21        (0x00 => _reserved0),
22        /// Watchdog Timer Control Register
23        (0x0C => ctl: ReadWrite<u16, WDTCTL::Register>),
24        (0x0E => @END),
25    }
26}
27
28register_bitfields![u16,
29    WDTCTL [
30        /// Watchdog timer interval select
31        WDTIS OFFSET(0) NUMBITS(3) [
32            /// Watchdog clock source / (2^(31)) (18:12:16 at 32.768 kHz)
33            WatchdogClockSource231181216At32768KHz = 0,
34            /// Watchdog clock source /(2^(27)) (01:08:16 at 32.768 kHz)
35            WatchdogClockSource227010816At32768KHz = 1,
36            /// Watchdog clock source /(2^(23)) (00:04:16 at 32.768 kHz)
37            WatchdogClockSource223000416At32768KHz = 2,
38            /// Watchdog clock source /(2^(19)) (00:00:16 at 32.768 kHz)
39            WatchdogClockSource219000016At32768KHz = 3,
40            /// Watchdog clock source /(2^(15)) (1 s at 32.768 kHz)
41            WatchdogClockSource2151SAt32768KHz = 4,
42            /// Watchdog clock source / (2^(13)) (250 ms at 32.768 kHz)
43            WatchdogClockSource213250MsAt32768KHz = 5,
44            /// Watchdog clock source / (2^(9)) (15.625 ms at 32.768 kHz)
45            WatchdogClockSource2915625MsAt32768KHz = 6,
46            /// Watchdog clock source / (2^(6)) (1.95 ms at 32.768 kHz)
47            WatchdogClockSource26195MsAt32768KHz = 7
48        ],
49        /// Watchdog timer counter clear
50        WDTCNTCL OFFSET(3) NUMBITS(1) [
51            /// No action
52            NoAction = 0,
53            /// WDTCNT = 0000h
54            WDTCNT0000h = 1
55        ],
56        /// Watchdog timer mode select
57        WDTTMSEL OFFSET(4) NUMBITS(1) [
58            /// Watchdog mode
59            WatchdogMode = 0,
60            /// Interval timer mode
61            IntervalTimerMode = 1
62        ],
63        /// Watchdog timer clock source select
64        WDTSSEL OFFSET(5) NUMBITS(2) [
65            /// SMCLK
66            SMCLK = 0,
67            /// ACLK
68            ACLK = 1,
69            /// VLOCLK
70            VLOCLK = 2,
71            /// BCLK
72            BCLK = 3
73        ],
74        /// Watchdog timer hold
75        WDTHOLD OFFSET(7) NUMBITS(1) [
76            /// Watchdog timer is not stopped
77            WatchdogTimerIsNotStopped = 0,
78            /// Watchdog timer is stopped
79            WatchdogTimerIsStopped = 1
80        ],
81        /// Watchdog timer password
82        WDTPW OFFSET(8) NUMBITS(8) []
83    ]
84];
85
86pub struct Wdt {
87    registers: StaticRef<WdtRegisters>,
88}
89
90impl Wdt {
91    pub const fn new() -> Wdt {
92        Wdt {
93            registers: WATCHDOG_BASE,
94        }
95    }
96
97    fn start(&self) {
98        // Enable the watchdog and clear the counter
99        self.registers
100            .ctl
101            .modify(WDTCTL::WDTPW.val(PASSWORD) + WDTCTL::WDTHOLD::CLEAR + WDTCTL::WDTCNTCL::SET);
102    }
103
104    pub fn disable(&self) {
105        self.registers
106            .ctl
107            .modify(WDTCTL::WDTPW.val(PASSWORD) + WDTCTL::WDTHOLD::SET);
108    }
109}
110
111impl kernel::platform::watchdog::WatchDog for Wdt {
112    fn setup(&self) {
113        // The clock-source of the watchdog is the SMCLK which runs at 1.5MHz. We configure a
114        // prescaler of 2^15 which results in a watchdog interval of approximately 22ms ->
115        // 2^15 / 1_500_000Hz = 32768 / 1_500_000 = 0.02184s
116
117        // According to the datasheet p. 759 section 17.2.3 it's necessary to disable the watchdog
118        // before setting it up and it's also necessary to set the WDTCNTCL bit within the same
119        // write cycle where the config is applied in order to avoid unexpected interrupts and
120        // resets.
121        self.disable();
122        // Set SMCLK as source -> 750kHz
123        // Enable Watchdog mode
124        // According to datasheet necessary
125        // Prescaler of 2^15
126        self.registers.ctl.modify(
127            WDTCTL::WDTPW.val(PASSWORD)
128                + WDTCTL::WDTSSEL::SMCLK
129                + WDTCTL::WDTTMSEL::WatchdogMode
130                + WDTCTL::WDTCNTCL::SET
131                + WDTCTL::WDTIS::WatchdogClockSource2151SAt32768KHz,
132        );
133
134        self.start();
135    }
136
137    fn suspend(&self) {
138        self.disable();
139    }
140
141    fn tickle(&self) {
142        // If the watchdog was disabled (self.suspend()), start it again.
143        if self.registers.ctl.is_set(WDTCTL::WDTHOLD) {
144            self.start();
145        } else {
146            self.registers
147                .ctl
148                .modify(WDTCTL::WDTPW.val(PASSWORD) + WDTCTL::WDTCNTCL::SET);
149        }
150    }
151}