atsamd_hal/peripherals/clock/
d11.rs

1//! Configuring the system clock sources.
2//!
3//! You will typically need to create an instance of `GenericClockController`
4//! before you can set up most of the peripherals on the atsamd21 device.
5//! The other types in this module are used to enforce at compile time
6//! that the peripherals have been correctly configured.
7#![allow(clippy::from_over_into)]
8
9use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
10
11use fugit::RateExtU32;
12
13use crate::pac::gclk::clkctrl::Genselect::*;
14use crate::pac::gclk::clkctrl::Idselect::*;
15use crate::pac::gclk::genctrl::Srcselect::*;
16use crate::pac::{self, Gclk, Nvmctrl, Pm, Sysctrl};
17use crate::time::Hertz;
18
19pub type ClockId = pac::gclk::clkctrl::Idselect;
20pub type ClockGenId = pac::gclk::clkctrl::Genselect;
21pub type ClockSource = pac::gclk::genctrl::Srcselect;
22
23/// Represents a configured clock generator.
24///
25/// Can be converted into the effective clock frequency.
26/// Its primary purpose is to be passed in to methods
27/// such as `GenericClockController::tcc2_tc3` to configure
28/// the clock for a peripheral.
29#[derive(Clone, Copy)]
30pub struct GClock {
31    gclk: ClockGenId,
32    freq: Hertz,
33}
34
35impl Into<Hertz> for GClock {
36    fn into(self) -> Hertz {
37        self.freq
38    }
39}
40
41struct State {
42    gclk: Gclk,
43}
44
45impl State {
46    fn reset_gclk(&mut self) {
47        self.gclk.ctrl().write(|w| w.swrst().set_bit());
48        while self.gclk.ctrl().read().swrst().bit_is_set()
49            || self.gclk.status().read().syncbusy().bit_is_set()
50        {}
51    }
52
53    fn wait_for_sync(&mut self) {
54        while self.gclk.status().read().syncbusy().bit_is_set() {}
55    }
56
57    fn set_gclk_divider_and_source(
58        &mut self,
59        gclk: ClockGenId,
60        divider: u16,
61        src: ClockSource,
62        improve_duty_cycle: bool,
63    ) {
64        // validate the divisor factor based on gclk ID (samd21 see 15.8.5, for samd11
65        // see 14.8.5)
66        let mut divisor_invalid = false;
67        if gclk == Gclk1 {
68            if divider as u32 >= 2_u32.pow(16) {
69                divisor_invalid = true;
70            }
71        } else if gclk == Gclk2 {
72            if divider >= 2_u16.pow(5) {
73                divisor_invalid = true;
74            }
75        } else if divider >= 2_u16.pow(8) {
76            divisor_invalid = true;
77        }
78        if divisor_invalid {
79            panic!("invalid divisor {} for Gclk {}", divider, gclk as u8);
80        }
81
82        self.gclk.gendiv().write(|w| unsafe {
83            w.id().bits(u8::from(gclk));
84            w.div().bits(divider)
85        });
86        self.wait_for_sync();
87
88        self.gclk.genctrl().write(|w| unsafe {
89            w.id().bits(u8::from(gclk));
90            w.src().bits(u8::from(src));
91            // divide directly by divider, rather than exponential
92            w.divsel().clear_bit();
93            w.idc().bit(improve_duty_cycle);
94            w.genen().set_bit();
95            w.oe().set_bit()
96        });
97        self.wait_for_sync();
98    }
99
100    fn enable_clock_generator(&mut self, clock: ClockId, generator: ClockGenId) {
101        self.gclk.clkctrl().write(|w| unsafe {
102            w.id().bits(u8::from(clock));
103            w.gen().bits(u8::from(generator));
104            w.clken().set_bit()
105        });
106        self.wait_for_sync();
107    }
108
109    fn configure_standby(&mut self, gclk: ClockGenId, enable: bool) {
110        // We must first read out the configuration of genctrl to read/modify/write it.
111        //   To do so, we must do an 8-bit write to GENCTRL.ID (ref 15.6.4.1 Indirect
112        //   Access). 32-bit write did not work.
113        unsafe {
114            let genctrl_ptr_u8: *mut u8 = self.gclk.genctrl().as_ptr() as *mut u8;
115            *genctrl_ptr_u8 = u8::from(gclk);
116        }
117        self.wait_for_sync();
118
119        // Now that the configuration is loaded, modify it
120        self.gclk.genctrl().modify(|_, w| w.runstdby().bit(enable));
121        self.wait_for_sync();
122    }
123}
124
125/// `GenericClockController` encapsulates the Gclk hardware.
126///
127/// It provides a type safe way to configure the system clocks.
128/// Initializing the `GenericClockController` instance configures
129/// the system to run at 48Mhz by setting gclk1 as a 32khz source
130/// and feeding it into the Dfll48 hardware which in turn drives
131/// gclk0 at 48Mhz.
132pub struct GenericClockController {
133    state: State,
134    gclks: [Hertz; 8],
135    used_clocks: u64,
136}
137
138impl GenericClockController {
139    /// Reset the clock controller, configure the system to run
140    /// at 48Mhz and reset various clock dividers.
141    pub fn with_internal_32kosc(
142        gclk: Gclk,
143        pm: &mut Pm,
144        sysctrl: &mut Sysctrl,
145        nvmctrl: &mut Nvmctrl,
146    ) -> Self {
147        Self::new_48mhz_from_32khz(gclk, pm, sysctrl, nvmctrl, false)
148    }
149
150    /// Reset the clock controller, configure the system to run
151    /// at 48Mhz and reset various clock dividers.
152    pub fn with_external_32kosc(
153        gclk: Gclk,
154        pm: &mut Pm,
155        sysctrl: &mut Sysctrl,
156        nvmctrl: &mut Nvmctrl,
157    ) -> Self {
158        Self::new_48mhz_from_32khz(gclk, pm, sysctrl, nvmctrl, true)
159    }
160
161    #[hal_macro_helper]
162    fn new_48mhz_from_32khz(
163        gclk: Gclk,
164        pm: &mut Pm,
165        sysctrl: &mut Sysctrl,
166        nvmctrl: &mut Nvmctrl,
167        use_external_crystal: bool,
168    ) -> Self {
169        let mut state = State { gclk };
170
171        set_flash_to_half_auto_wait_state(nvmctrl);
172        #[hal_cfg("clock-d21")]
173        set_flash_manual_write(nvmctrl);
174        enable_gclk_apb(pm);
175        if use_external_crystal {
176            enable_external_32kosc(sysctrl);
177        } else {
178            enable_internal_32kosc(sysctrl);
179        }
180
181        state.reset_gclk();
182
183        // Enable a 32khz source -> Gclk1
184        if use_external_crystal {
185            state.set_gclk_divider_and_source(Gclk1, 1, Xosc32k, false);
186        } else {
187            state.set_gclk_divider_and_source(Gclk1, 1, Osc32k, false);
188        }
189
190        // Feed 32khz into the Dfll48
191        state.enable_clock_generator(Dfll48, Gclk1);
192        // Enable the Dfll48
193        configure_and_enable_dfll48m(sysctrl, use_external_crystal);
194        // Feed Dfll48 into the main clock
195        state.set_gclk_divider_and_source(Gclk0, 1, Dfll48m, true);
196        // We are now running at 48Mhz
197
198        // Reset various dividers back to 1
199        sysctrl.osc8m().modify(|_, w| {
200            w.presc()._0();
201            w.ondemand().clear_bit()
202        });
203        pm.cpusel().write(|w| w.cpudiv().div1());
204        pm.apbasel().write(|w| w.apbadiv().div1());
205        pm.apbbsel().write(|w| w.apbbdiv().div1());
206        pm.apbcsel().write(|w| w.apbcdiv().div1());
207
208        Self {
209            state,
210            gclks: [
211                OSC48M_FREQ,
212                OSC32K_FREQ,
213                0.Hz(),
214                0.Hz(),
215                0.Hz(),
216                0.Hz(),
217                0.Hz(),
218                0.Hz(),
219            ],
220            used_clocks: 1u64 << u8::from(ClockId::Dfll48),
221        }
222    }
223
224    /// Reset the clock controller, configure the system to run at 8Mhz from
225    /// internal 8 MHz RC clock (no PLL) and reset various clock dividers.
226    #[hal_macro_helper]
227    pub fn with_internal_8mhz(
228        gclk: Gclk,
229        pm: &mut Pm,
230        sysctrl: &mut Sysctrl,
231        nvmctrl: &mut Nvmctrl,
232    ) -> Self {
233        let mut state = State { gclk };
234
235        // No wait states needed <= 24 MHz @ 3.3v (ref. 37.12 NVM characteristics)
236        #[hal_cfg("clock-d21")]
237        set_flash_manual_write(nvmctrl);
238
239        // Get rid of unused warning
240        #[hal_cfg("clock-d11")]
241        let _ = nvmctrl;
242
243        enable_gclk_apb(pm);
244
245        state.reset_gclk();
246
247        // Enable 8 MHz source -> Gclk0
248        state.set_gclk_divider_and_source(Gclk0, 1, Osc8m, false);
249
250        // Reset various dividers back to 1
251        sysctrl.osc8m().modify(|_, w| {
252            w.presc()._0();
253            w.ondemand().clear_bit()
254        });
255        pm.cpusel().write(|w| w.cpudiv().div1());
256        pm.apbasel().write(|w| w.apbadiv().div1());
257        pm.apbbsel().write(|w| w.apbbdiv().div1());
258        pm.apbcsel().write(|w| w.apbcdiv().div1());
259
260        Self {
261            state,
262            gclks: [
263                OSC8M_FREQ,
264                0.Hz(),
265                0.Hz(),
266                0.Hz(),
267                0.Hz(),
268                0.Hz(),
269                0.Hz(),
270                0.Hz(),
271            ],
272            used_clocks: 0,
273        }
274    }
275
276    /// Returns a `GClock` for gclk0, the system clock generator at 48Mhz
277    pub fn gclk0(&mut self) -> GClock {
278        GClock {
279            gclk: Gclk0,
280            freq: self.gclks[0],
281        }
282    }
283
284    /// Returns a `GClock` for gclk1, the 32Khz oscillator.
285    pub fn gclk1(&mut self) -> GClock {
286        GClock {
287            gclk: Gclk1,
288            freq: self.gclks[1],
289        }
290    }
291
292    /// Returns the `GClock` for the specified clock generator.
293    /// If that clock generator has not yet been configured,
294    /// returns None.
295    pub fn get_gclk(&mut self, gclk: ClockGenId) -> Option<GClock> {
296        let idx = u8::from(gclk) as usize;
297        if self.gclks[idx].to_Hz() == 0 {
298            None
299        } else {
300            Some(GClock {
301                gclk,
302                freq: self.gclks[idx],
303            })
304        }
305    }
306
307    /// Configures a clock generator with the specified divider and
308    /// source.
309    /// `divider` is a linear divider to be applied to the clock
310    /// source.  While the hardware also supports an exponential divider,
311    /// this function doesn't expose that functionality at this time.
312    /// `improve_duty_cycle` is a boolean that, when set to true, enables
313    /// a 5o/50 duty cycle for odd divider values.
314    /// Returns a `GClock` for the configured clock generator.
315    /// Returns `None` if the clock generator has already been configured.
316    pub fn configure_gclk_divider_and_source(
317        &mut self,
318        gclk: ClockGenId,
319        divider: u16,
320        src: ClockSource,
321        improve_duty_cycle: bool,
322    ) -> Option<GClock> {
323        let idx = u8::from(gclk) as usize;
324        if self.gclks[idx].to_Hz() != 0 {
325            return None;
326        }
327        self.state
328            .set_gclk_divider_and_source(gclk, divider, src, improve_duty_cycle);
329        let freq: Hertz = match src {
330            Xosc32k | Osc32k | Osculp32k => OSC32K_FREQ,
331            Gclkgen1 => self.gclks[1],
332            Osc8m => OSC8M_FREQ,
333            Dfll48m => OSC48M_FREQ,
334            Dpll96m => 96.MHz(),
335            Gclkin | Xosc => unimplemented!(),
336        };
337        self.gclks[idx] = freq / divider as u32;
338        Some(GClock { gclk, freq })
339    }
340
341    /// Enables or disables the given GClk from operation in standby.
342    pub fn configure_standby(&mut self, gclk: ClockGenId, enable: bool) {
343        self.state.configure_standby(gclk, enable)
344    }
345}
346
347macro_rules! clock_generator {
348    ($(($id:ident, $Type:ident, $clock:ident),)+) => {
349
350$(
351/// A typed token that indicates that the clock for the peripheral(s)
352/// with the matching name has been configured.
353///
354/// The effective clock frequency is available via the `freq` method,
355/// or by converting the object into a `Hertz` instance.
356/// The peripheral initialization code will typically require passing
357/// in this object to prove at compile time that the clock has been
358/// correctly initialized.
359#[derive(Debug)]
360pub struct $Type {
361    freq: Hertz,
362}
363
364impl $Type {
365    /// Returns the frequency of the configured clock
366    pub fn freq(&self) -> Hertz {
367        self.freq
368    }
369}
370impl Into<Hertz> for $Type {
371    fn into(self) -> Hertz {
372        self.freq
373    }
374}
375)+
376
377impl GenericClockController {
378    $(
379    /// Configure the clock for peripheral(s) that match the name
380    /// of this function to use the specific clock generator.
381    ///
382    /// The `GClock` parameter may be one of default clocks
383    /// return from `gclk0()`, `gclk1()` or a clock configured
384    /// by the host application using the `configure_gclk_divider_and_source`
385    /// method.
386    /// Returns a typed token that proves that the clock has been configured;
387    /// the peripheral initialization code will typically require that this
388    /// clock token be passed in to ensure that the clock has been initialized
389    /// appropriately.
390    /// Returns `None` is the specified generic clock has already been
391    /// configured.
392    pub fn $id(&mut self, generator: &GClock) -> Option<$Type> {
393        let bits: u64 = 1<<u8::from(ClockId::$clock) as u64;
394        if (self.used_clocks & bits) != 0 {
395            return None;
396        }
397        self.used_clocks |= bits;
398
399        self.state.enable_clock_generator(ClockId::$clock, generator.gclk);
400        let freq = self.gclks[u8::from(generator.gclk) as usize];
401        Some($Type{freq})
402    }
403    )+
404}
405    }
406}
407
408// samd11
409#[hal_cfg("clock-d11")]
410clock_generator!(
411    (tcc0, Tcc0Clock, Tcc0),
412    (tc1_tc2, Tc1Tc2Clock, Tc1Tc2),
413    (sercom0_core, Sercom0CoreClock, Sercom0Core),
414    (sercom1_core, Sercom1CoreClock, Sercom1Core),
415    (sercom2_core, Sercom2CoreClock, Sercom2Core),
416    (rtc, RtcClock, Rtc),
417    (adc, AdcClock, Adc),
418    (wdt, WdtClock, Wdt),
419    (eic, EicClock, Eic),
420    (usb, UsbClock, Usb),
421    (evsys0, Evsys0Clock, Evsys0),
422    (evsys1, Evsys1Clock, Evsys1),
423    (evsys2, Evsys2Clock, Evsys2),
424    (evsys3, Evsys3Clock, Evsys3),
425    (evsys4, Evsys4Clock, Evsys4),
426    (evsys5, Evsys5Clock, Evsys5),
427    (ac_ana, AcAnaClock, AcAna),
428    (ac_dig, AcDigClock, AcDig),
429    (dac, DacClock, Dac),
430);
431// samd21
432#[hal_cfg("clock-d21")]
433clock_generator!(
434    (tcc0_tcc1, Tcc0Tcc1Clock, Tcc0Tcc1),
435    (tcc2_tc3, Tcc2Tc3Clock, Tcc2Tc3),
436    (tc4_tc5, Tc4Tc5Clock, Tc4Tc5),
437    (tc6_tc7, Tc6Tc7Clock, Tc6Tc7),
438    (sercom0_core, Sercom0CoreClock, Sercom0Core),
439    (sercom1_core, Sercom1CoreClock, Sercom1Core),
440    (sercom2_core, Sercom2CoreClock, Sercom2Core),
441    (sercom3_core, Sercom3CoreClock, Sercom3Core),
442    (sercom4_core, Sercom4CoreClock, Sercom4Core),
443    (sercom5_core, Sercom5CoreClock, Sercom5Core),
444    (usb, UsbClock, Usb),
445    (rtc, RtcClock, Rtc),
446    (adc, AdcClock, Adc),
447    (wdt, WdtClock, Wdt),
448    (eic, EicClock, Eic),
449    (evsys0, Evsys0Clock, Evsys0),
450    (evsys1, Evsys1Clock, Evsys1),
451    (evsys2, Evsys2Clock, Evsys2),
452    (evsys3, Evsys3Clock, Evsys3),
453    (evsys4, Evsys4Clock, Evsys4),
454    (evsys5, Evsys5Clock, Evsys5),
455    (evsys6, Evsys6Clock, Evsys6),
456    (evsys7, Evsys7Clock, Evsys7),
457    (evsys8, Evsys8Clock, Evsys8),
458    (evsys9, Evsys9Clock, Evsys9),
459    (evsys10, Evsys10Clock, Evsys10),
460    (evsys11, Evsys11Clock, Evsys11),
461    (ac_ana, AcAnaClock, AcAna),
462    (ac_dig, AcDigClock, AcDig),
463    (dac, DacClock, Dac),
464    (i2s0, I2S0Clock, I2s0),
465    (i2s1, I2S1Clock, I2s1),
466);
467
468/// The frequency of the 48Mhz source.
469pub const OSC48M_FREQ: Hertz = Hertz::Hz(48_000_000);
470/// The frequency of the 8 Mhz source.
471pub const OSC8M_FREQ: Hertz = Hertz::Hz(8_000_000);
472/// The frequency of the 32Khz source.
473pub const OSC32K_FREQ: Hertz = Hertz::Hz(32_768);
474
475fn set_flash_to_half_auto_wait_state(nvmctrl: &mut Nvmctrl) {
476    nvmctrl.ctrlb().modify(|_, w| w.rws().half());
477}
478
479/// Prevent automatic writes to flash by pointers to flash area
480#[hal_cfg("clock-d21")]
481fn set_flash_manual_write(nvmctrl: &mut Nvmctrl) {
482    nvmctrl.ctrlb().modify(|_, w| w.manw().set_bit());
483}
484
485fn enable_gclk_apb(pm: &mut Pm) {
486    pm.apbamask().modify(|_, w| w.gclk_().set_bit());
487}
488
489/// Turn on the internal 32hkz oscillator
490pub fn enable_internal_32kosc(sysctrl: &mut Sysctrl) {
491    let calibration = super::calibration::osc32k_cal();
492    sysctrl.osc32k().write(|w| {
493        unsafe {
494            w.ondemand().clear_bit();
495            w.calib().bits(calibration);
496            // 6 here means: use 66 cycles of OSC32k to start up this oscillator
497            w.startup().bits(6);
498        }
499        w.en32k().set_bit();
500        w.enable().set_bit();
501        w.runstdby().set_bit()
502    });
503    while sysctrl.pclksr().read().osc32krdy().bit_is_clear() {
504        // Wait for the oscillator to stabilize
505    }
506}
507
508/// Turn on the external 32hkz oscillator
509pub fn enable_external_32kosc(sysctrl: &mut Sysctrl) {
510    sysctrl.xosc32k().modify(|_, w| {
511        unsafe {
512            // 6 here means: use 64k cycles of OSCULP32k to start up this oscillator
513            w.startup().bits(6);
514        }
515        w.ondemand().clear_bit();
516        // Enable 32khz output
517        w.en32k().set_bit();
518        // Crystal connected to xin32/xout32
519        w.xtalen().set_bit();
520        w.runstdby().set_bit()
521    });
522    sysctrl.xosc32k().modify(|_, w| w.enable().set_bit());
523    while sysctrl.pclksr().read().xosc32krdy().bit_is_clear() {
524        // Wait for the oscillator to stabilize
525    }
526}
527
528fn wait_for_dfllrdy(sysctrl: &mut Sysctrl) {
529    while sysctrl.pclksr().read().dfllrdy().bit_is_clear() {}
530}
531
532/// Configure the dfll48m to operate at 48Mhz
533#[hal_macro_helper]
534fn configure_and_enable_dfll48m(sysctrl: &mut Sysctrl, use_external_crystal: bool) {
535    // Turn it off while we configure it.
536    // Note that we need to turn off on-demand mode and
537    // disable it here, rather than just reseting the ctrl
538    // register, otherwise our configuration attempt fails.
539    sysctrl.dfllctrl().write(|w| w.ondemand().clear_bit());
540    wait_for_dfllrdy(sysctrl);
541
542    if use_external_crystal {
543        sysctrl.dfllmul().write(|w| unsafe {
544            w.cstep().bits(31);
545            w.fstep().bits(511);
546            // scaling factor between the clocks
547            w.mul().bits(((48_000_000u32 + 32768 / 2) / 32768) as u16)
548        });
549
550        // Turn it on
551        sysctrl.dfllctrl().write(|w| {
552            // always on
553            w.ondemand().clear_bit();
554
555            // closed loop mode
556            w.mode().set_bit();
557
558            w.waitlock().set_bit();
559
560            // Disable quick lock
561            w.qldis().set_bit()
562        });
563    } else {
564        // Apply calibration
565        let coarse = super::calibration::dfll48m_coarse_cal();
566        let fine = 0x1ff;
567
568        sysctrl.dfllval().write(|w| unsafe {
569            w.coarse().bits(coarse);
570            w.fine().bits(fine)
571        });
572
573        sysctrl.dfllmul().write(|w| unsafe {
574            w.cstep().bits(coarse / 4);
575            w.fstep().bits(10);
576            // scaling factor for 1 kHz Usb SOF signal
577            w.mul().bits((48_000_000u32 / 1000) as u16)
578        });
579
580        // Turn it on
581        sysctrl.dfllctrl().write(|w| {
582            // always on
583            w.ondemand().clear_bit();
584
585            // closed loop mode
586            w.mode().set_bit();
587
588            // chill cycle disable
589            w.ccdis().set_bit();
590
591            // usb correction
592            w.usbcrm().set_bit();
593
594            // bypass coarse lock (have calibration data)
595            w.bplckc().set_bit()
596        });
597    }
598
599    wait_for_dfllrdy(sysctrl);
600
601    // and finally enable it!
602    sysctrl.dfllctrl().modify(|_, w| w.enable().set_bit());
603
604    #[hal_cfg("clock-d21")]
605    if use_external_crystal {
606        // wait for lock
607        while sysctrl.pclksr().read().dflllckc().bit_is_clear()
608            || sysctrl.pclksr().read().dflllckf().bit_is_clear()
609        {}
610    }
611
612    wait_for_dfllrdy(sysctrl);
613}