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