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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
//! Dynamic Frequency Switching control
//!
//! #TODO
//! - Sleep functionality/Awake lock

use super::Error;
use crate::prelude::*;

/// maximum number of callbacks
pub const MAX_CALLBACKS: usize = 10;

/// number of cpu, apb, awake and pll_d2 locks
#[derive(Copy, Clone, Debug)]
pub struct Locks {
    cpu: usize,
    apb: usize,
    awake: usize,
    pll_d2: usize,
}

static DFS_MUTEX: CriticalSectionSpinLockMutex<Locks> = CriticalSectionSpinLockMutex::new(Locks {
    cpu: 0,
    apb: 0,
    awake: 0,
    pll_d2: 0,
});

/// DFS structure
pub(super) struct DFS {
    callbacks: [&'static dyn Fn(super::CPUSource, Hertz, Hertz, super::CPUSource, Hertz, Hertz);
        MAX_CALLBACKS],
    nr_callbacks: CriticalSectionSpinLockMutex<usize>,
}

impl DFS {
    pub(crate) fn new() -> Self {
        DFS {
            callbacks: [&|_, _, _, _, _, _| {}; MAX_CALLBACKS],

            nr_callbacks: CriticalSectionSpinLockMutex::new(0),
        }
    }
}

/// A RAII implementation of a "scoped lock" for CPU frequency.
/// When this structure is dropped (falls out of scope), the lock will be unlocked.
/// This structure is created by the lock_cpu_frequency method on ClockControlConfig
pub struct LockCPU {}

/// A RAII implementation of a "scoped lock" for APB frequency.
/// When this structure is dropped (falls out of scope), the lock will be unlocked.
/// This structure is created by the lock_apb_frequency method on ClockControlConfig
pub struct LockAPB {}

/// A RAII implementation of a "scoped lock" for Awake state.
/// When this structure is dropped (falls out of scope), the lock will be unlocked.
/// This structure is created by the lock_awake method on ClockControlConfig
pub struct LockAwake {}

/// A RAII implementation of a "scoped lock" for PLL/2 frequency.
/// When this structure is dropped (falls out of scope), the lock will be unlocked.
/// This structure is created by the lock_plld2 method on ClockControlConfig
pub struct LockPllD2 {}

/// Drop of the RAII to unlock the CPU frequency
impl<'a> Drop for LockCPU {
    fn drop(&mut self) {
        unsafe {
            super::CLOCK_CONTROL
                .as_mut()
                .unwrap()
                .unlock_cpu_frequency();
        }
    }
}

/// Drop of the RAII to unlock the APB frequency
impl<'a> Drop for LockAPB {
    fn drop(&mut self) {
        unsafe {
            super::CLOCK_CONTROL
                .as_mut()
                .unwrap()
                .unlock_apb_frequency();
        }
    }
}

/// Drop of the RAII to unlock the Awake state
impl<'a> Drop for LockAwake {
    fn drop(&mut self) {
        unsafe {
            super::CLOCK_CONTROL.as_mut().unwrap().unlock_awake();
        }
    }
}

/// Drop of the RAII to unlock the PLL/2 frequency
impl<'a> Drop for LockPllD2 {
    fn drop(&mut self) {
        unsafe {
            super::CLOCK_CONTROL.as_mut().unwrap().unlock_plld2();
        }
    }
}

impl<'a> super::ClockControl {
    /// call all the callbacks
    fn do_callbacks(
        &self,
        cpu_source_before: super::CPUSource,
        cpu_frequency_before: Hertz,
        apb_frequency_before: Hertz,
        cpu_source_after: super::CPUSource,
        cpu_frequency_after: Hertz,
        apb_frequency_after: Hertz,
    ) {
        if cpu_source_after == cpu_source_before
            && cpu_frequency_after == cpu_frequency_before
            && apb_frequency_after == apb_frequency_before
        {
            return;
        }

        // copy the callbacks to prevent needing to have interrupts disabled the entire time
        // as callback cannot be deleted this is ok.
        let (nr, callbacks) = (&self.dfs.nr_callbacks).lock(|nr| (*nr, self.dfs.callbacks));

        for i in 0..nr {
            callbacks[i](
                cpu_source_before,
                cpu_frequency_before,
                apb_frequency_before,
                cpu_source_after,
                cpu_frequency_after,
                apb_frequency_after,
            );
        }
    }

    /// lock the CPU to maximum frequency
    pub(crate) fn lock_cpu_frequency(&'a mut self) -> LockCPU {
        (&DFS_MUTEX).lock(|data| {
            data.cpu += 1;

            if data.cpu == 1 {
                if data.apb == 0 || self.cpu_frequency_locked > self.cpu_frequency_apb_locked {
                    let cpu_source_before = self.cpu_source;
                    let cpu_frequency_before = self.cpu_frequency;
                    let apb_frequency_before = self.apb_frequency;

                    self.set_cpu_frequency_locked(data.pll_d2 > 0).unwrap();

                    self.do_callbacks(
                        cpu_source_before,
                        cpu_frequency_before,
                        apb_frequency_before,
                        self.cpu_source,
                        self.cpu_frequency,
                        self.apb_frequency,
                    );
                }
            }
        });
        LockCPU {}
    }

    /// unlock the CPU frequency
    fn unlock_cpu_frequency(&'a mut self) {
        (&DFS_MUTEX).lock(|data| {
            data.cpu -= 1;

            if data.cpu == 0 {
                let cpu_source_before = self.cpu_source;
                let cpu_frequency_before = self.cpu_frequency;
                let apb_frequency_before = self.apb_frequency;

                if data.apb == 0 {
                    self.set_cpu_frequency_default(data.pll_d2 > 0).unwrap();
                } else {
                    self.set_cpu_frequency_apb_locked(data.pll_d2 > 0).unwrap();
                }

                self.do_callbacks(
                    cpu_source_before,
                    cpu_frequency_before,
                    apb_frequency_before,
                    self.cpu_source,
                    self.cpu_frequency,
                    self.apb_frequency,
                );
            }
        });
    }

    // lock the CPU to APB frequency
    pub(crate) fn lock_apb_frequency(&'a mut self) -> LockAPB {
        (&DFS_MUTEX).lock(|data| {
            data.apb += 1;

            if data.apb == 1 {
                if data.cpu == 0 || self.cpu_frequency_apb_locked > self.cpu_frequency_locked {
                    let cpu_source_before = self.cpu_source;
                    let cpu_frequency_before = self.cpu_frequency;
                    let apb_frequency_before = self.apb_frequency;

                    self.set_cpu_frequency_apb_locked(data.pll_d2 > 0).unwrap();

                    self.do_callbacks(
                        cpu_source_before,
                        cpu_frequency_before,
                        apb_frequency_before,
                        self.cpu_source,
                        self.cpu_frequency,
                        self.apb_frequency,
                    );
                }
            }
        });
        LockAPB {}
    }

    /// unlock the CPU from APB
    fn unlock_apb_frequency(&'a mut self) {
        (&DFS_MUTEX).lock(|data| {
            data.apb -= 1;

            if data.apb == 0 {
                let cpu_source_before = self.cpu_source;
                let cpu_frequency_before = self.cpu_frequency;
                let apb_frequency_before = self.apb_frequency;

                if data.cpu == 0 {
                    self.set_cpu_frequency_default(data.pll_d2 > 0).unwrap();
                } else {
                    self.set_cpu_frequency_locked(data.pll_d2 > 0).unwrap();
                }

                self.do_callbacks(
                    cpu_source_before,
                    cpu_frequency_before,
                    apb_frequency_before,
                    self.cpu_source,
                    self.cpu_frequency,
                    self.apb_frequency,
                );
            }
        });
    }

    // lock in awake state
    pub(crate) fn lock_awake(&'a mut self) -> LockAwake {
        (&DFS_MUTEX).lock(|data| {
            data.awake += 1;
        });

        // TODO: implement actual locking
        LockAwake {}
    }

    /// unlock from the awake state
    fn unlock_awake(&'a mut self) {
        (&DFS_MUTEX).lock(|data| {
            data.awake -= 1;

            // TODO: implement actual unlocking
            unimplemented!();
        });
    }

    /// lock the PLL/2 frequency
    pub(crate) fn lock_plld2(&'a mut self) -> LockPllD2 {
        (&DFS_MUTEX).lock(|data| {
            data.pll_d2 += 1;
            if data.pll_d2 == 1 && self.pll_frequency == super::FREQ_OFF {
                let cpu_source_before = self.cpu_source;
                let cpu_frequency_before = self.cpu_frequency;
                let apb_frequency_before = self.apb_frequency;

                self.pll_enable(false).unwrap();

                self.do_callbacks(
                    cpu_source_before,
                    cpu_frequency_before,
                    apb_frequency_before,
                    self.cpu_source,
                    self.cpu_frequency,
                    self.apb_frequency,
                );
            }
        });

        LockPllD2 {}
    }

    /// unlock the PLL/2 frequency
    fn unlock_plld2(&'a mut self) {
        (&DFS_MUTEX).lock(|data| {
            data.pll_d2 -= 1;

            if data.pll_d2 == 0 && self.cpu_source() != super::CPUSource::PLL {
                let cpu_source_before = self.cpu_source;
                let cpu_frequency_before = self.cpu_frequency;
                let apb_frequency_before = self.apb_frequency;

                self.pll_disable();

                self.do_callbacks(
                    cpu_source_before,
                    cpu_frequency_before,
                    apb_frequency_before,
                    self.cpu_source,
                    self.cpu_frequency,
                    self.apb_frequency,
                );
            }
        });
    }

    /// Add callback which will be called when clock speeds are changed.
    ///
    /// NOTE: these callbacks are called in an interrupt free environment,
    /// so should be as short as possible
    ///
    /// TODO: at the moment only static lifetime callbacks are allowed
    pub(crate) fn add_callback<F>(&mut self, f: &'static F) -> Result<(), Error>
    where
        F: Fn(super::CPUSource, Hertz, Hertz, super::CPUSource, Hertz, Hertz),
    {
        // need to disable interrupts, because otherwise deadlock can arise
        // when interrupt is called after mutex is obtained and interrupt
        // routine also adds callback

        let callbacks = &mut self.dfs.callbacks;
        (&self.dfs.nr_callbacks).lock(|nr| {
            if *nr >= MAX_CALLBACKS {
                return Err(Error::TooManyCallbacks);
            }

            callbacks[*nr] = f;
            *nr += 1;
            Ok(())
        })
    }

    /// Get the current count of the PCU, APB, Awake and PLL/2 locks
    ///
    /// Note that this function cannot be used form within a callback
    /// as it tries to lock the mutex, leading to a dead-lock.
    pub fn get_lock_count(&self) -> Locks {
        (&DFS_MUTEX).lock(|data| *data)
    }
}