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}