stm32f303xc/
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Window watchdog timer

use crate::rcc;
use core::cell::Cell;
use kernel::platform::chip::ClockInterface;
use kernel::utilities::registers::interfaces::ReadWriteable;
use kernel::utilities::registers::{register_bitfields, ReadWrite};
use kernel::utilities::StaticRef;

const WINDOW_WATCHDOG_BASE: StaticRef<WwdgRegisters> =
    unsafe { StaticRef::new(0x4000_2C00 as *const WwdgRegisters) };

#[repr(C)]
pub struct WwdgRegisters {
    cr: ReadWrite<u32, Control::Register>,
    cfr: ReadWrite<u32, Config::Register>,
    sr: ReadWrite<u32, Status::Register>,
}

register_bitfields![u32,
    Control [
        /// Watch dog activation
        /// Set by software and only cleared by hardware after a reset.
        /// When set, the watchdog can generate a reset.
        WDGA OFFSET(7) NUMBITS(1) [],
        /// 7 bit counter
        /// These bits contain the value of the watchdog counter. It is
        /// decremented every 4096 * 2^WDGTB PCLK cycles. A reset is produced
        /// when it is decremented from 0x40 to 0x3F (T[6] becomes cleared).
        T OFFSET(0) NUMBITS(7) []
    ],
    Config [
        /// Early wakeup interrupt
        /// When set, interrupt occurs whenever the counter reaches the value
        /// of 0x40. This interrupt is only cleared by hardware after a reset.
        EWI OFFSET(9) NUMBITS(1) [],
        /// Timer base
        /// This allows modifying the time base of the prescaler.
        WDGTB OFFSET(7) NUMBITS(2) [
            /// CK Counter Clock (PCLK div 4096) div 1
            DIVONE = 0,
            /// CK Counter Clock (PCLK div 4096) div 2
            DIVTWO = 1,
            /// CK Counter Clock (PCLK div 4096) div 4
            DIVFOUR = 2,
            /// CK Counter Clock (PCLK div 4096) div 8
            DIVEIGHT = 3
        ],
        /// 7 bit window value
        /// These bits contain the window value to be compared to the
        /// downcounter.
        W OFFSET(0) NUMBITS(7) []
    ],
    Status [
        /// Early wakeup interrupt flag
        /// This is set when the counter has reached the value 0x40. It must be
        /// cleared by software by writing 0. This bit is also set when the
        /// interrupt is not enabled.
        EWIF OFFSET(0) NUMBITS(1) []
    ]
];

pub struct WindoWdg<'a> {
    registers: StaticRef<WwdgRegisters>,
    clock: WdgClock<'a>,
    enabled: Cell<bool>,
}

impl<'a> WindoWdg<'a> {
    pub const fn new(rcc: &'a rcc::Rcc) -> Self {
        Self {
            registers: WINDOW_WATCHDOG_BASE,
            clock: WdgClock(rcc::PeripheralClock::new(
                rcc::PeripheralClockType::APB1(rcc::PCLK1::WWDG),
                rcc,
            )),
            enabled: Cell::new(false),
        }
    }

    pub fn enable(&self) {
        self.enabled.set(true);
    }

    fn set_window(&self, value: u32) {
        // Set the window value to the biggest possible one.
        self.registers.cfr.modify(Config::W.val(value));
    }

    /// Modifies the time base of the prescaler.
    /// 0 - decrements the watchdog every clock cycle
    /// 1 - decrements the watchdog every 2nd clock cycle
    /// 2 - decrements the watchdog every 4th clock cycle
    /// 3 - decrements the watchdog every 8th clock cycle
    fn set_prescaler(&self, time_base: u8) {
        match time_base {
            0 => self.registers.cfr.modify(Config::WDGTB::DIVONE),
            1 => self.registers.cfr.modify(Config::WDGTB::DIVTWO),
            2 => self.registers.cfr.modify(Config::WDGTB::DIVFOUR),
            3 => self.registers.cfr.modify(Config::WDGTB::DIVEIGHT),
            _ => {}
        }
    }

    pub fn start(&self) {
        // Enable the APB1 clock for the watchdog.
        self.clock.enable();

        // This disables the window feature. Set this to a value smaller than
        // 0x7F if you want to enable it.
        self.set_window(0x7F);
        self.set_prescaler(3);

        // Set the T[6] bit to avoid a reset when the watchdog is activated.
        self.tickle();

        // With the APB1 clock running at 36Mhz we are getting timeout value of
        // t_WWDG = (1 / 36000) * 4096 * 2^3 * (63 + 1) = 58ms
        self.registers.cr.modify(Control::WDGA::SET);
    }

    pub fn tickle(&self) {
        // Uses 63 as the value the watchdog starts counting from.
        self.registers.cr.modify(Control::T.val(0x7F));
    }
}

struct WdgClock<'a>(rcc::PeripheralClock<'a>);

impl ClockInterface for WdgClock<'_> {
    fn is_enabled(&self) -> bool {
        self.0.is_enabled()
    }

    fn enable(&self) {
        self.0.enable();
    }

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

impl kernel::platform::watchdog::WatchDog for WindoWdg<'_> {
    fn setup(&self) {
        if self.enabled.get() {
            self.start();
        }
    }

    fn tickle(&self) {
        if self.enabled.get() {
            self.tickle();
        }
    }

    fn suspend(&self) {
        if self.enabled.get() {
            self.clock.disable();
        }
    }

    fn resume(&self) {
        if self.enabled.get() {
            self.clock.enable();
        }
    }
}