stm32f303xc/
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//! Window watchdog timer
6
7use crate::rcc;
8use core::cell::Cell;
9use kernel::platform::chip::ClockInterface;
10use kernel::utilities::registers::interfaces::ReadWriteable;
11use kernel::utilities::registers::{register_bitfields, ReadWrite};
12use kernel::utilities::StaticRef;
13
14const WINDOW_WATCHDOG_BASE: StaticRef<WwdgRegisters> =
15    unsafe { StaticRef::new(0x4000_2C00 as *const WwdgRegisters) };
16
17#[repr(C)]
18pub struct WwdgRegisters {
19    cr: ReadWrite<u32, Control::Register>,
20    cfr: ReadWrite<u32, Config::Register>,
21    sr: ReadWrite<u32, Status::Register>,
22}
23
24register_bitfields![u32,
25    Control [
26        /// Watch dog activation
27        /// Set by software and only cleared by hardware after a reset.
28        /// When set, the watchdog can generate a reset.
29        WDGA OFFSET(7) NUMBITS(1) [],
30        /// 7 bit counter
31        /// These bits contain the value of the watchdog counter. It is
32        /// decremented every 4096 * 2^WDGTB PCLK cycles. A reset is produced
33        /// when it is decremented from 0x40 to 0x3F (T[6] becomes cleared).
34        T OFFSET(0) NUMBITS(7) []
35    ],
36    Config [
37        /// Early wakeup interrupt
38        /// When set, interrupt occurs whenever the counter reaches the value
39        /// of 0x40. This interrupt is only cleared by hardware after a reset.
40        EWI OFFSET(9) NUMBITS(1) [],
41        /// Timer base
42        /// This allows modifying the time base of the prescaler.
43        WDGTB OFFSET(7) NUMBITS(2) [
44            /// CK Counter Clock (PCLK div 4096) div 1
45            DIVONE = 0,
46            /// CK Counter Clock (PCLK div 4096) div 2
47            DIVTWO = 1,
48            /// CK Counter Clock (PCLK div 4096) div 4
49            DIVFOUR = 2,
50            /// CK Counter Clock (PCLK div 4096) div 8
51            DIVEIGHT = 3
52        ],
53        /// 7 bit window value
54        /// These bits contain the window value to be compared to the
55        /// downcounter.
56        W OFFSET(0) NUMBITS(7) []
57    ],
58    Status [
59        /// Early wakeup interrupt flag
60        /// This is set when the counter has reached the value 0x40. It must be
61        /// cleared by software by writing 0. This bit is also set when the
62        /// interrupt is not enabled.
63        EWIF OFFSET(0) NUMBITS(1) []
64    ]
65];
66
67pub struct WindoWdg<'a> {
68    registers: StaticRef<WwdgRegisters>,
69    clock: WdgClock<'a>,
70    enabled: Cell<bool>,
71}
72
73impl<'a> WindoWdg<'a> {
74    pub const fn new(rcc: &'a rcc::Rcc) -> Self {
75        Self {
76            registers: WINDOW_WATCHDOG_BASE,
77            clock: WdgClock(rcc::PeripheralClock::new(
78                rcc::PeripheralClockType::APB1(rcc::PCLK1::WWDG),
79                rcc,
80            )),
81            enabled: Cell::new(false),
82        }
83    }
84
85    pub fn enable(&self) {
86        self.enabled.set(true);
87    }
88
89    fn set_window(&self, value: u32) {
90        // Set the window value to the biggest possible one.
91        self.registers.cfr.modify(Config::W.val(value));
92    }
93
94    /// Modifies the time base of the prescaler.
95    /// 0 - decrements the watchdog every clock cycle
96    /// 1 - decrements the watchdog every 2nd clock cycle
97    /// 2 - decrements the watchdog every 4th clock cycle
98    /// 3 - decrements the watchdog every 8th clock cycle
99    fn set_prescaler(&self, time_base: u8) {
100        match time_base {
101            0 => self.registers.cfr.modify(Config::WDGTB::DIVONE),
102            1 => self.registers.cfr.modify(Config::WDGTB::DIVTWO),
103            2 => self.registers.cfr.modify(Config::WDGTB::DIVFOUR),
104            3 => self.registers.cfr.modify(Config::WDGTB::DIVEIGHT),
105            _ => {}
106        }
107    }
108
109    pub fn start(&self) {
110        // Enable the APB1 clock for the watchdog.
111        self.clock.enable();
112
113        // This disables the window feature. Set this to a value smaller than
114        // 0x7F if you want to enable it.
115        self.set_window(0x7F);
116        self.set_prescaler(3);
117
118        // Set the T[6] bit to avoid a reset when the watchdog is activated.
119        self.tickle();
120
121        // With the APB1 clock running at 36Mhz we are getting timeout value of
122        // t_WWDG = (1 / 36000) * 4096 * 2^3 * (63 + 1) = 58ms
123        self.registers.cr.modify(Control::WDGA::SET);
124    }
125
126    pub fn tickle(&self) {
127        // Uses 63 as the value the watchdog starts counting from.
128        self.registers.cr.modify(Control::T.val(0x7F));
129    }
130}
131
132struct WdgClock<'a>(rcc::PeripheralClock<'a>);
133
134impl ClockInterface for WdgClock<'_> {
135    fn is_enabled(&self) -> bool {
136        self.0.is_enabled()
137    }
138
139    fn enable(&self) {
140        self.0.enable();
141    }
142
143    fn disable(&self) {
144        self.0.disable();
145    }
146}
147
148impl kernel::platform::watchdog::WatchDog for WindoWdg<'_> {
149    fn setup(&self) {
150        if self.enabled.get() {
151            self.start();
152        }
153    }
154
155    fn tickle(&self) {
156        if self.enabled.get() {
157            self.tickle();
158        }
159    }
160
161    fn suspend(&self) {
162        if self.enabled.get() {
163            self.clock.disable();
164        }
165    }
166
167    fn resume(&self) {
168        if self.enabled.get() {
169            self.clock.enable();
170        }
171    }
172}