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}