msp432/
wdt.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Watchdog Timer (WDT)

use kernel::utilities::registers::interfaces::{ReadWriteable, Readable};
use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
use kernel::utilities::StaticRef;

const WATCHDOG_BASE: StaticRef<WdtRegisters> =
    unsafe { StaticRef::new(0x4000_4800u32 as *const WdtRegisters) };

// Every write access has to set this 'password' in the upper 8 bit of the
// register, otherwise the watchdog resets the whole system
const PASSWORD: u16 = 0x5A;

register_structs! {
    /// WDT_A
    WdtRegisters {
        (0x00 => _reserved0),
        /// Watchdog Timer Control Register
        (0x0C => ctl: ReadWrite<u16, WDTCTL::Register>),
        (0x0E => @END),
    }
}

register_bitfields![u16,
    WDTCTL [
        /// Watchdog timer interval select
        WDTIS OFFSET(0) NUMBITS(3) [
            /// Watchdog clock source / (2^(31)) (18:12:16 at 32.768 kHz)
            WatchdogClockSource231181216At32768KHz = 0,
            /// Watchdog clock source /(2^(27)) (01:08:16 at 32.768 kHz)
            WatchdogClockSource227010816At32768KHz = 1,
            /// Watchdog clock source /(2^(23)) (00:04:16 at 32.768 kHz)
            WatchdogClockSource223000416At32768KHz = 2,
            /// Watchdog clock source /(2^(19)) (00:00:16 at 32.768 kHz)
            WatchdogClockSource219000016At32768KHz = 3,
            /// Watchdog clock source /(2^(15)) (1 s at 32.768 kHz)
            WatchdogClockSource2151SAt32768KHz = 4,
            /// Watchdog clock source / (2^(13)) (250 ms at 32.768 kHz)
            WatchdogClockSource213250MsAt32768KHz = 5,
            /// Watchdog clock source / (2^(9)) (15.625 ms at 32.768 kHz)
            WatchdogClockSource2915625MsAt32768KHz = 6,
            /// Watchdog clock source / (2^(6)) (1.95 ms at 32.768 kHz)
            WatchdogClockSource26195MsAt32768KHz = 7
        ],
        /// Watchdog timer counter clear
        WDTCNTCL OFFSET(3) NUMBITS(1) [
            /// No action
            NoAction = 0,
            /// WDTCNT = 0000h
            WDTCNT0000h = 1
        ],
        /// Watchdog timer mode select
        WDTTMSEL OFFSET(4) NUMBITS(1) [
            /// Watchdog mode
            WatchdogMode = 0,
            /// Interval timer mode
            IntervalTimerMode = 1
        ],
        /// Watchdog timer clock source select
        WDTSSEL OFFSET(5) NUMBITS(2) [
            /// SMCLK
            SMCLK = 0,
            /// ACLK
            ACLK = 1,
            /// VLOCLK
            VLOCLK = 2,
            /// BCLK
            BCLK = 3
        ],
        /// Watchdog timer hold
        WDTHOLD OFFSET(7) NUMBITS(1) [
            /// Watchdog timer is not stopped
            WatchdogTimerIsNotStopped = 0,
            /// Watchdog timer is stopped
            WatchdogTimerIsStopped = 1
        ],
        /// Watchdog timer password
        WDTPW OFFSET(8) NUMBITS(8) []
    ]
];

pub struct Wdt {
    registers: StaticRef<WdtRegisters>,
}

impl Wdt {
    pub const fn new() -> Wdt {
        Wdt {
            registers: WATCHDOG_BASE,
        }
    }

    fn start(&self) {
        // Enable the watchdog and clear the counter
        self.registers
            .ctl
            .modify(WDTCTL::WDTPW.val(PASSWORD) + WDTCTL::WDTHOLD::CLEAR + WDTCTL::WDTCNTCL::SET);
    }

    pub fn disable(&self) {
        self.registers
            .ctl
            .modify(WDTCTL::WDTPW.val(PASSWORD) + WDTCTL::WDTHOLD::SET);
    }
}

impl kernel::platform::watchdog::WatchDog for Wdt {
    fn setup(&self) {
        // The clock-source of the watchdog is the SMCLK which runs at 1.5MHz. We configure a
        // prescaler of 2^15 which results in a watchdog interval of approximately 22ms ->
        // 2^15 / 1_500_000Hz = 32768 / 1_500_000 = 0.02184s

        // According to the datasheet p. 759 section 17.2.3 it's necessary to disable the watchdog
        // before setting it up and it's also necessary to set the WDTCNTCL bit within the same
        // write cycle where the config is applied in order to avoid unexpected interrupts and
        // resets.
        self.disable();
        // Set SMCLK as source -> 750kHz
        // Enable Watchdog mode
        // According to datasheet necessary
        // Prescaler of 2^15
        self.registers.ctl.modify(
            WDTCTL::WDTPW.val(PASSWORD)
                + WDTCTL::WDTSSEL::SMCLK
                + WDTCTL::WDTTMSEL::WatchdogMode
                + WDTCTL::WDTCNTCL::SET
                + WDTCTL::WDTIS::WatchdogClockSource2151SAt32768KHz,
        );

        self.start();
    }

    fn suspend(&self) {
        self.disable();
    }

    fn tickle(&self) {
        // If the watchdog was disabled (self.suspend()), start it again.
        if self.registers.ctl.is_set(WDTCTL::WDTHOLD) {
            self.start();
        } else {
            self.registers
                .ctl
                .modify(WDTCTL::WDTPW.val(PASSWORD) + WDTCTL::WDTCNTCL::SET);
        }
    }
}