1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include "fsl_clock.h"
17 #include "fsl_power.h"
18 #include "pw_clock_tree/clock_tree.h"
19 
20 namespace pw::clock_tree {
21 
22 /// Class implementing an FRO clock source.
23 class ClockMcuxpressoFro final
24     : public ClockSource<ElementNonBlockingCannotFail> {
25  public:
26   /// Constructor specifying the FRO divider output to manage.
ClockMcuxpressoFro(clock_fro_output_en_t fro_output)27   constexpr ClockMcuxpressoFro(clock_fro_output_en_t fro_output)
28       : fro_output_(fro_output) {}
29 
30  private:
31   /// Enable this FRO divider.
DoEnable()32   Status DoEnable() final {
33     CLOCK_EnableFroClk(CLKCTL0->FRODIVOEN | fro_output_);
34     return OkStatus();
35   }
36 
37   /// Disable this FRO divider.
DoDisable()38   Status DoDisable() final {
39     CLOCK_EnableFroClk(CLKCTL0->FRODIVOEN & ~fro_output_);
40     return OkStatus();
41   }
42 
43   /// FRO divider.
44   const uint32_t fro_output_;
45 };
46 
47 /// Class implementing the low power oscillator clock source.
48 class ClockMcuxpressoLpOsc final
49     : public ClockSource<ElementNonBlockingCannotFail> {
50  private:
51   /// Enable low power oscillator.
DoEnable()52   Status DoEnable() final {
53     // Power up the 1MHz low power oscillator power domain.
54     POWER_DisablePD(kPDRUNCFG_PD_LPOSC);
55     // POWER_ApplyPD() is not necessary for LPOSC_PD.
56 
57     // Wait for the low power oscillator to stabilize.
58     CLOCK_EnableLpOscClk();
59     return OkStatus();
60   }
61 
62   /// Disable low power oscillator.
DoDisable()63   Status DoDisable() final {
64     // Power down the 1MHz low power oscillator power domain.
65     POWER_EnablePD(kPDRUNCFG_PD_LPOSC);
66     // POWER_ApplyPD() is not necessary for LPOSC_PD.
67     return OkStatus();
68   }
69 };
70 
71 /// Class template implementing the MCLK IN clock source.
72 ///
73 /// Template argument `ElementType` can be of class `ElementBlocking` or
74 /// `ElementNonBlockingCannotFail`.
75 template <typename ElementType>
76 class ClockMcuxpressoMclk final : public DependentElement<ElementType> {
77  public:
78   /// Constructor specifying the MCLK IN clock frequency in Hz and
79   /// the dependent clock tree element to enable the MCLK clock source.
ClockMcuxpressoMclk(ElementType & source,uint32_t frequency)80   constexpr ClockMcuxpressoMclk(ElementType& source, uint32_t frequency)
81       : DependentElement<ElementType>(source), frequency_(frequency) {}
82 
83  private:
84   /// Set MCLK IN clock frequency.
DoEnable()85   Status DoEnable() final {
86     // Set global that stores external MCLK IN clock frequency.
87     CLOCK_SetMclkFreq(frequency_);
88     return OkStatus();
89   }
90 
91   /// Set MCLK IN clock frequency to 0 Hz.
DoDisable()92   Status DoDisable() final {
93     // Set global that stores external MCLK IN clock frequency to zero.
94     CLOCK_SetMclkFreq(0);
95     return OkStatus();
96   }
97 
98   /// MCLK IN frequency in Hz.
99   uint32_t frequency_;
100 };
101 
102 /// Alias for a blocking MCLK IN clock tree element.
103 /// This class should be used if the MCLK IN clock source depends on
104 /// another blocking clock tree element to enable the MCLK IN clock source.
105 using ClockMcuxpressoMclkBlocking = ClockMcuxpressoMclk<ElementBlocking>;
106 
107 /// Alias for a non-blocking MCLK IN clock tree element where updates cannot
108 /// fail.
109 using ClockMcuxpressoMclkNonBlocking =
110     ClockMcuxpressoMclk<ElementNonBlockingCannotFail>;
111 
112 /// Class template implementing the CLK IN pin clock source and selecting
113 /// it as an input source for OSC Clock source.
114 ///
115 /// Template argument `ElementType` can be of class `ElementBlocking` or
116 /// `ElementNonBlockingCannotFail`.
117 template <typename ElementType>
118 class ClockMcuxpressoClkIn final : public DependentElement<ElementType> {
119  public:
120   /// Constructor specifying the CLK IN pin clock frequency in Hz and
121   /// the dependent clock tree element to enable the CLK IN pin clock source.
ClockMcuxpressoClkIn(ElementType & source,uint32_t frequency)122   constexpr ClockMcuxpressoClkIn(ElementType& source, uint32_t frequency)
123       : DependentElement<ElementType>(source), frequency_(frequency) {}
124 
125  private:
126   /// Set CLK IN clock frequency.
DoEnable()127   Status DoEnable() final {
128     // Set global that stores external CLK IN pin clock frequency.
129     CLOCK_SetClkinFreq(frequency_);
130 
131     // OSC clock source selector ClkIn.
132     const uint8_t kCLOCK_OscClkIn = CLKCTL0_SYSOSCBYPASS_SEL(1);
133     CLKCTL0->SYSOSCBYPASS = kCLOCK_OscClkIn;
134     return OkStatus();
135   }
136 
137   /// Set CLK IN clock frequency to 0 Hz.
DoDisable()138   Status DoDisable() final {
139     // Set global that stores external CLK IN pin clock frequency to zero.
140     CLOCK_SetClkinFreq(0);
141 
142     // Set OSC clock source selector None, which gates output to reduce power.
143     const uint8_t kCLOCK_OscNone = CLKCTL0_SYSOSCBYPASS_SEL(7);
144     CLKCTL0->SYSOSCBYPASS = kCLOCK_OscNone;
145     return OkStatus();
146   }
147 
148   /// CLK IN frequency in Hz.
149   uint32_t frequency_;
150 };
151 
152 /// Alias for a blocking CLK IN pin clock tree element.
153 /// This class should be used if the CLK IN pin clock source depends on
154 /// another blocking clock tree element to enable the CLK IN pin clock source.
155 using ClockMcuxpressoClkInBlocking = ClockMcuxpressoClkIn<ElementBlocking>;
156 
157 /// Alias for a non-blocking CLK IN pin clock tree element where updates cannot
158 /// fail.
159 using ClockMcuxpressoClkInNonBlocking =
160     ClockMcuxpressoClkIn<ElementNonBlockingCannotFail>;
161 
162 /// Class template implementing the FRG clock tree element.
163 ///
164 /// Template argument `ElementType` can be of class `ElementBlocking` or
165 /// `ElementNonBlockingCannotFail`.
166 template <typename ElementType>
167 class ClockMcuxpressoFrg final : public DependentElement<ElementType> {
168  public:
169   /// Constructor specifying the source clock and FRG configuration.
ClockMcuxpressoFrg(ElementType & source,const clock_frg_clk_config_t & config)170   constexpr ClockMcuxpressoFrg(ElementType& source,
171                                const clock_frg_clk_config_t& config)
172       : DependentElement<ElementType>(source), config_(config) {}
173 
174  private:
175   // FRG clock source selector None, which gates output to reduce power.
176   // The None source selector is not defined in the SDK.
177   const uint8_t kCLOCK_FrgNone = 7;
178 
179   /// Enable FRG configuration.
DoEnable()180   Status DoEnable() final {
181     CLOCK_SetFRGClock(&config_);
182     return OkStatus();
183   }
184 
185   /// Disable FRG configuration.
DoDisable()186   Status DoDisable() final {
187     clock_frg_clk_config_t disable_config = config_;
188     static_assert(sizeof(disable_config.sfg_clock_src) ==
189                   sizeof(kCLOCK_FrgNone));
190     disable_config.sfg_clock_src =
191         static_cast<decltype(disable_config.sfg_clock_src)>(kCLOCK_FrgNone);
192     CLOCK_SetFRGClock(&disable_config);
193     return OkStatus();
194   }
195 
196   /// FRG clock configuration to enable FRG component.
197   const clock_frg_clk_config_t& config_;
198 };
199 
200 /// Alias for a blocking FRG clock tree element.
201 using ClockMcuxpressoFrgBlocking = ClockMcuxpressoFrg<ElementBlocking>;
202 
203 /// Alias for a non-blocking FRG clock tree element where updates cannot fail.
204 using ClockMcuxpressoFrgNonBlocking =
205     ClockMcuxpressoFrg<ElementNonBlockingCannotFail>;
206 
207 /// Class template implementing the clock selector element.
208 ///
209 /// Template argument `ElementType` can be of class `ElementBlocking` or
210 /// `ElementNonBlockingCannotFail`.
211 template <typename ElementType>
212 class ClockMcuxpressoSelector : public DependentElement<ElementType> {
213  public:
214   /// Constructor specifying the source clock and the selector value
215   /// when the selector should get enabled, and the selector value when
216   /// the selector should get disabled to save power.
ClockMcuxpressoSelector(ElementType & source,clock_attach_id_t selector_enable,clock_attach_id_t selector_disable)217   constexpr ClockMcuxpressoSelector(ElementType& source,
218                                     clock_attach_id_t selector_enable,
219                                     clock_attach_id_t selector_disable)
220       : DependentElement<ElementType>(source),
221         selector_enable_(selector_enable),
222         selector_disable_(selector_disable) {}
223 
224  private:
225   /// Enable selector.
DoEnable()226   Status DoEnable() final {
227     CLOCK_AttachClk(selector_enable_);
228     return OkStatus();
229   }
230 
231   /// Disable selector.
DoDisable()232   Status DoDisable() final {
233     CLOCK_AttachClk(selector_disable_);
234     return OkStatus();
235   }
236 
237   /// Enable selector value.
238   clock_attach_id_t selector_enable_;
239   /// Disable selector value.
240   clock_attach_id_t selector_disable_;
241 };
242 
243 /// Alias for a blocking clock selector clock tree element.
244 using ClockMcuxpressoSelectorBlocking =
245     ClockMcuxpressoSelector<ElementBlocking>;
246 
247 /// Alias for a non-blocking clock selector clock tree element where updates
248 /// cannot fail.
249 using ClockMcuxpressoSelectorNonBlocking =
250     ClockMcuxpressoSelector<ElementNonBlockingCannotFail>;
251 
252 /// Class template implementing the clock divider element.
253 ///
254 /// Template argument `ElementType` can be of class `ElementBlocking` or
255 /// `ElementNonBlockingCannotFail`.
256 template <typename ElementType>
257 class ClockMcuxpressoDivider final : public ClockDividerElement<ElementType> {
258  public:
259   /// Constructor specifying the source clock, the name of the divder and
260   /// the divider setting.
ClockMcuxpressoDivider(ElementType & source,clock_div_name_t divider_name,uint32_t divider)261   constexpr ClockMcuxpressoDivider(ElementType& source,
262                                    clock_div_name_t divider_name,
263                                    uint32_t divider)
264       : ClockDividerElement<ElementType>(source, divider),
265         divider_name_(divider_name) {}
266 
267  private:
268   /// Set the divider configuration.
DoEnable()269   Status DoEnable() final {
270     CLOCK_SetClkDiv(divider_name_, this->divider());
271     return OkStatus();
272   }
273 
274   /// Name of divider.
275   clock_div_name_t divider_name_;
276 };
277 
278 /// Alias for a blocking clock divider clock tree element.
279 using ClockMcuxpressoDividerBlocking = ClockMcuxpressoDivider<ElementBlocking>;
280 
281 /// Alias for a non-blocking clock divider clock tree element where updates
282 /// cannot fail.
283 using ClockMcuxpressoDividerNonBlocking =
284     ClockMcuxpressoDivider<ElementNonBlockingCannotFail>;
285 
286 /// Class template implementing the audio pll clock element.
287 ///
288 /// The Audio PLL can either operate in the enabled mode where the PLL
289 /// and the phase fractional divider are enabled, or it can operate in
290 /// bypass mode, where both PLL and phase fractional divider are
291 /// clock gated.
292 /// When the Audio PLL clock tree gets disabled, both PLL and phase fractional
293 /// divider will be clock gated.
294 ///
295 /// Template argument `ElementType` can be of class `ElementBlocking` or
296 /// `ElementNonBlockingCannotFail`.
297 template <typename ElementType>
298 class ClockMcuxpressoAudioPll : public DependentElement<ElementType> {
299  public:
300   /// Constructor specifying the configuration for the enabled Audio PLL.
ClockMcuxpressoAudioPll(ElementType & source,const clock_audio_pll_config_t & config,uint8_t audio_pfd_divider)301   constexpr ClockMcuxpressoAudioPll(ElementType& source,
302                                     const clock_audio_pll_config_t& config,
303                                     uint8_t audio_pfd_divider)
304       : DependentElement<ElementType>(source),
305         config_(&config),
306         audio_pfd_divider_(audio_pfd_divider) {}
307 
308   /// Constructor to place the Audio PLL into bypass mode.
ClockMcuxpressoAudioPll(ElementType & source,audio_pll_src_t bypass_source)309   constexpr ClockMcuxpressoAudioPll(ElementType& source,
310                                     audio_pll_src_t bypass_source)
311       : DependentElement<ElementType>(source), bypass_source_(bypass_source) {}
312 
313  private:
314   /// Configures and enables the audio PLL if `config_` is set, otherwise places
315   /// the audio PLL in bypass mode.
DoEnable()316   Status DoEnable() override {
317     // If `config_` is specified, the PLL should be enabled and the phase
318     // fractional divider PFD0 needs to get configured, otherwise the PLL
319     // operates in bypass mode.
320     if (config_ != nullptr) {
321       // Configure Audio PLL clock source.
322       CLOCK_InitAudioPll(config_);
323       CLOCK_InitAudioPfd(kCLOCK_Pfd0, audio_pfd_divider_);
324     } else {
325       // PLL operates in bypass mode.
326       CLKCTL1->AUDIOPLL0CLKSEL = bypass_source_;
327       CLKCTL1->AUDIOPLL0CTL0 |= CLKCTL1_AUDIOPLL0CTL0_BYPASS_MASK;
328     }
329     return OkStatus();
330   }
331 
332   /// Disables the audio PLL logic.
DoDisable()333   Status DoDisable() override {
334     if (config_ != nullptr) {
335       // Clock gate the phase fractional divider PFD0.
336       CLOCK_DeinitAudioPfd(kCLOCK_Pfd0);
337     }
338 
339     // Power down Audio PLL
340     CLOCK_DeinitAudioPll();
341     return OkStatus();
342   }
343 
344   /// Optional audio PLL configuration.
345   const clock_audio_pll_config_t* config_ = nullptr;
346 
347   /// Optional audio kCLOCK_Pfd0 clock divider value.
348   const uint8_t audio_pfd_divider_ = 0;
349 
350   /// Optional audio PLL bypass clock source.
351   const audio_pll_src_t bypass_source_ = kCLOCK_AudioPllNone;
352 };
353 
354 /// Alias for a blocking audio PLL clock tree element.
355 using ClockMcuxpressoAudioPllBlocking =
356     ClockMcuxpressoAudioPll<ElementBlocking>;
357 
358 /// Alias for a non-blocking audio PLL clock tree element where updates
359 /// cannot fail.
360 using ClockMcuxpressoAudioPllNonBlocking =
361     ClockMcuxpressoAudioPll<ElementNonBlockingCannotFail>;
362 
363 /// Class template implementing the Rtc clock tree element.
364 ///
365 /// Template argument `ElementType` can be of class `ElementBlocking` or
366 /// `ElementNonBlockingCannotFail`.
367 template <typename ElementType>
368 class ClockMcuxpressoRtc final : public DependentElement<ElementType> {
369  public:
370   /// Constructor specifying the dependent clock tree element to enable the
371   /// Rtc clock source.
ClockMcuxpressoRtc(ElementType & source)372   constexpr ClockMcuxpressoRtc(ElementType& source)
373       : DependentElement<ElementType>(source) {}
374 
375  private:
376   /// Enable 32 kHz RTC oscillator
DoEnable()377   Status DoEnable() final {
378     // Enable 32kHZ output of RTC oscillator.
379     CLOCK_EnableOsc32K(true);
380     return OkStatus();
381   }
382 
383   /// Disable 32 kHz RTS oscillator.
DoDisable()384   Status DoDisable() final {
385     // Disable 32KHz output of RTC oscillator.
386     CLOCK_EnableOsc32K(false);
387     return OkStatus();
388   }
389 };
390 
391 /// Alias for a blocking Rtc clock tree element.
392 /// This class should be used if the Rtc clock source depends on
393 /// another blocking clock tree element to enable the Rtc clock source.
394 using ClockMcuxpressoRtcBlocking = ClockMcuxpressoRtc<ElementBlocking>;
395 
396 /// Alias for a non-blocking Rtc clock tree element where updates
397 /// cannot fail.
398 using ClockMcuxpressoRtcNonBlocking =
399     ClockMcuxpressoRtc<ElementNonBlockingCannotFail>;
400 
401 /// Class template implementing the `clock_ip_name_t` clocks.
402 /// Managing `clock_ip_name_t` clocks with the clock tree allows to
403 /// save power when `FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL` is set.
404 ///
405 /// Template argument `ElementType` can be of class `ElementBlocking` or
406 /// `ElementNonBlockingCannotFail`.
407 template <typename ElementType>
408 class ClockMcuxpressoClockIp final : public DependentElement<ElementType> {
409  public:
410   /// Constructor specifying the dependent clock tree element to enable the
411   /// `clock_ip_name_t` clock source.
ClockMcuxpressoClockIp(ElementType & source,clock_ip_name_t clock)412   constexpr ClockMcuxpressoClockIp(ElementType& source, clock_ip_name_t clock)
413       : DependentElement<ElementType>(source), clock_(clock) {}
414 
415  private:
416   /// Enable the clock.
DoEnable()417   Status DoEnable() final {
418     CLOCK_EnableClock(clock_);
419     return OkStatus();
420   }
421 
422   /// Disable the clock.
DoDisable()423   Status DoDisable() final {
424     CLOCK_DisableClock(clock_);
425     return OkStatus();
426   }
427 
428   clock_ip_name_t clock_;
429 };
430 
431 /// Alias for a blocking ClockIp clock tree element.
432 /// This class should be used if the ClockIp clock source depends on
433 /// another blocking clock tree element to enable the ClockIp clock source.
434 using ClockMcuxpressoClockIpBlocking = ClockMcuxpressoClockIp<ElementBlocking>;
435 
436 /// Alias for a non-blocking ClockIp clock tree element where updates
437 /// cannot fail.
438 using ClockMcuxpressoClockIpNonBlocking =
439     ClockMcuxpressoClockIp<ElementNonBlockingCannotFail>;
440 }  // namespace pw::clock_tree
441