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_DisablePD(kPDRUNCFG_PD_LPOSC); /* Power on LPOSC (1MHz) */ 54 // POWER_ApplyPD() is not necessary for LPOSC_PD. 55 CLOCK_EnableLpOscClk(); /* Wait until LPOSC stable */ 56 return OkStatus(); 57 } 58 59 /// Disable low power oscillator. DoDisable()60 Status DoDisable() final { 61 POWER_EnablePD(kPDRUNCFG_PD_LPOSC); /* Power down LPOSC (1MHz). */ 62 // POWER_ApplyPD() is not necessary for LPOSC_PD. 63 return OkStatus(); 64 } 65 }; 66 67 /// Class template implementing the MCLK IN clock source. 68 /// 69 /// Template argument `ElementType` can be of class `ElementBlocking` or 70 /// `ElementNonBlockingCannotFail`. 71 template <typename ElementType> 72 class ClockMcuxpressoMclk final : public DependentElement<ElementType> { 73 public: 74 /// Constructor specifying the MCLK IN clock frequency in Hz and 75 /// the dependent clock tree element to enable the MCLK clock source. ClockMcuxpressoMclk(ElementType & source,uint32_t frequency)76 constexpr ClockMcuxpressoMclk(ElementType& source, uint32_t frequency) 77 : DependentElement<ElementType>(source), frequency_(frequency) {} 78 79 private: 80 /// Set MCLK IN clock frequency. DoEnable()81 Status DoEnable() final { 82 CLOCK_SetMclkFreq(frequency_); /* Sets external MCLKIN freq */ 83 return OkStatus(); 84 } 85 86 /// Set MCLK IN clock frequency to 0 Hz. DoDisable()87 Status DoDisable() final { 88 CLOCK_SetMclkFreq(0); /* Sets external MCLKIN freq */ 89 return OkStatus(); 90 } 91 92 /// MCLK IN frequency. 93 uint32_t frequency_; 94 }; 95 96 /// Alias for a blocking MCLK IN clock tree element. 97 using ClockMcuxpressoMclkBlocking = ClockMcuxpressoMclk<ElementBlocking>; 98 99 /// Alias for a non-blocking MCLK IN clock tree element where updates cannot 100 /// fail. 101 using ClockMcuxpressoMclkNonBlocking = 102 ClockMcuxpressoMclk<ElementNonBlockingCannotFail>; 103 104 /// Class template implementing the CLK IN pin clock source and selecting 105 /// it as an input source for OSC Clock source. 106 /// 107 /// Template argument `ElementType` can be of class `ElementBlocking` or 108 /// `ElementNonBlockingCannotFail`. 109 template <typename ElementType> 110 class ClockMcuxpressoClkIn final : public DependentElement<ElementType> { 111 public: 112 /// Constructor specifying the CLK IN pin clock frequency in Hz and 113 /// the dependent clock tree element to enable the CLK IN pin clock source. ClockMcuxpressoClkIn(ElementType & source,uint32_t frequency)114 constexpr ClockMcuxpressoClkIn(ElementType& source, uint32_t frequency) 115 : DependentElement<ElementType>(source), frequency_(frequency) {} 116 117 private: 118 /// Set CLK IN clock frequency. DoEnable()119 Status DoEnable() final { 120 CLOCK_SetClkinFreq( 121 frequency_); /*!< Sets CLK_IN pin clock frequency in Hz */ 122 123 // OSC clock source selector ClkIn. 124 const uint8_t kCLOCK_OscClkIn = CLKCTL0_SYSOSCBYPASS_SEL(1); 125 CLKCTL0->SYSOSCBYPASS = kCLOCK_OscClkIn; 126 return OkStatus(); 127 } 128 129 /// Set CLK IN clock frequency to 0 Hz. DoDisable()130 Status DoDisable() final { 131 CLOCK_SetClkinFreq(0); /*!< Sets CLK_IN pin clock frequency in Hz */ 132 133 // OSC clock source selector None, which gates output to reduce power. 134 const uint8_t kCLOCK_OscNone = CLKCTL0_SYSOSCBYPASS_SEL(7); 135 CLKCTL0->SYSOSCBYPASS = kCLOCK_OscNone; 136 return OkStatus(); 137 } 138 139 /// CLK IN frequency. 140 uint32_t frequency_; 141 }; 142 143 /// Alias for a blocking CLK IN pin clock tree element. 144 using ClockMcuxpressoClkInBlocking = ClockMcuxpressoClkIn<ElementBlocking>; 145 146 /// Alias for a non-blocking CLK IN pin clock tree element where updates cannot 147 /// fail. 148 using ClockMcuxpressoClkInNonBlocking = 149 ClockMcuxpressoClkIn<ElementNonBlockingCannotFail>; 150 151 /// Class template implementing the FRG clock tree element. 152 /// 153 /// Template argument `ElementType` can be of class `ElementBlocking` or 154 /// `ElementNonBlockingCannotFail`. 155 template <typename ElementType> 156 class ClockMcuxpressoFrg final : public DependentElement<ElementType> { 157 public: 158 /// Constructor specifying the source clock and FRG configuration. ClockMcuxpressoFrg(ElementType & source,const clock_frg_clk_config_t & config)159 constexpr ClockMcuxpressoFrg(ElementType& source, 160 const clock_frg_clk_config_t& config) 161 : DependentElement<ElementType>(source), config_(config) {} 162 163 private: 164 // FRG clock source selector None, which gates output to reduce power. 165 // The None source selector is not defined in the SDK. 166 const uint8_t kCLOCK_FrgNone = 7; 167 168 /// Enable FRG configuration. DoEnable()169 Status DoEnable() final { 170 CLOCK_SetFRGClock(&config_); 171 return OkStatus(); 172 } 173 174 /// Disable FRG configuration. DoDisable()175 Status DoDisable() final { 176 clock_frg_clk_config_t disable_config = config_; 177 static_assert(sizeof(disable_config.sfg_clock_src) == 178 sizeof(kCLOCK_FrgNone)); 179 disable_config.sfg_clock_src = 180 static_cast<decltype(disable_config.sfg_clock_src)>(kCLOCK_FrgNone); 181 CLOCK_SetFRGClock(&disable_config); 182 return OkStatus(); 183 } 184 185 /// FRG clock configuration to enable FRG component. 186 const clock_frg_clk_config_t& config_; 187 }; 188 189 /// Alias for a blocking FRG clock tree element. 190 using ClockMcuxpressoFrgBlocking = ClockMcuxpressoFrg<ElementBlocking>; 191 192 /// Alias for a non-blocking FRG clock tree element where updates cannot fail. 193 using ClockMcuxpressoFrgNonBlocking = 194 ClockMcuxpressoFrg<ElementNonBlockingCannotFail>; 195 196 /// Class template implementing the clock selector element. 197 /// 198 /// Template argument `ElementType` can be of class `ElementBlocking` or 199 /// `ElementNonBlockingCannotFail`. 200 template <typename ElementType> 201 class ClockMcuxpressoSelector : public DependentElement<ElementType> { 202 public: 203 /// Constructor specifying the source clock and the selector value 204 /// when the selector should get enabled, and the selector value when 205 /// the selector should get disabled to save power. ClockMcuxpressoSelector(ElementType & source,clock_attach_id_t selector_enable,clock_attach_id_t selector_disable)206 constexpr ClockMcuxpressoSelector(ElementType& source, 207 clock_attach_id_t selector_enable, 208 clock_attach_id_t selector_disable) 209 : DependentElement<ElementType>(source), 210 selector_enable_(selector_enable), 211 selector_disable_(selector_disable) {} 212 213 private: 214 /// Enable selector. DoEnable()215 Status DoEnable() final { 216 CLOCK_AttachClk(selector_enable_); 217 return OkStatus(); 218 } 219 220 /// Disable selector. DoDisable()221 Status DoDisable() final { 222 CLOCK_AttachClk(selector_disable_); 223 return OkStatus(); 224 } 225 226 /// Enable selector value. 227 clock_attach_id_t selector_enable_; 228 /// Disable selector value. 229 clock_attach_id_t selector_disable_; 230 }; 231 232 /// Alias for a blocking clock selector clock tree element. 233 using ClockMcuxpressoSelectorBlocking = 234 ClockMcuxpressoSelector<ElementBlocking>; 235 236 /// Alias for a non-blocking clock selector clock tree element where updates 237 /// cannot fail. 238 using ClockMcuxpressoSelectorNonBlocking = 239 ClockMcuxpressoSelector<ElementNonBlockingCannotFail>; 240 241 /// Class template implementing the clock divider element. 242 /// 243 /// Template argument `ElementType` can be of class `ElementBlocking` or 244 /// `ElementNonBlockingCannotFail`. 245 template <typename ElementType> 246 class ClockMcuxpressoDivider final : public ClockDividerElement<ElementType> { 247 public: 248 /// Constructor specifying the source clock, the name of the divder and 249 /// the divider setting. ClockMcuxpressoDivider(ElementType & source,clock_div_name_t divider_name,uint32_t divider)250 constexpr ClockMcuxpressoDivider(ElementType& source, 251 clock_div_name_t divider_name, 252 uint32_t divider) 253 : ClockDividerElement<ElementType>(source, divider), 254 divider_name_(divider_name) {} 255 256 private: 257 /// Set the divider configuration. DoEnable()258 Status DoEnable() final { 259 CLOCK_SetClkDiv(divider_name_, this->divider()); 260 return OkStatus(); 261 } 262 263 /// Name of divider. 264 clock_div_name_t divider_name_; 265 }; 266 267 /// Alias for a blocking clock divider clock tree element. 268 using ClockMcuxpressoDividerBlocking = ClockMcuxpressoDivider<ElementBlocking>; 269 270 /// Alias for a non-blocking clock divider clock tree element where updates 271 /// cannot fail. 272 using ClockMcuxpressoDividerNonBlocking = 273 ClockMcuxpressoDivider<ElementNonBlockingCannotFail>; 274 275 /// Class template implementing the audio pll clock element. 276 /// 277 /// The Audio PLL can either operate in the enabled mode where the PLL 278 /// and the phase fractional divider are enabled, or it can operate in 279 /// bypass mode, where both PLL and phase fractional divider are 280 /// clock gated. 281 /// When the Audio PLL clock tree gets disabled, both PLL and phase fractional 282 /// divider will be clock gated. 283 /// 284 /// Template argument `ElementType` can be of class `ElementBlocking` or 285 /// `ElementNonBlockingCannotFail`. 286 template <typename ElementType> 287 class ClockMcuxpressoAudioPll : public DependentElement<ElementType> { 288 public: 289 /// Constructor specifying the configuration for the enabled Audio PLL. ClockMcuxpressoAudioPll(ElementType & source,const clock_audio_pll_config_t & config,uint8_t audio_pfd_divider)290 constexpr ClockMcuxpressoAudioPll(ElementType& source, 291 const clock_audio_pll_config_t& config, 292 uint8_t audio_pfd_divider) 293 : DependentElement<ElementType>(source), 294 config_(&config), 295 audio_pfd_divider_(audio_pfd_divider) {} 296 297 /// Constructor to place the Audio PLL into bypass mode. ClockMcuxpressoAudioPll(ElementType & source,audio_pll_src_t bypass_source)298 constexpr ClockMcuxpressoAudioPll(ElementType& source, 299 audio_pll_src_t bypass_source) 300 : DependentElement<ElementType>(source), bypass_source_(bypass_source) {} 301 302 private: 303 /// Configures and enables the audio PLL if `config_` is set, otherwise places 304 /// the audio PLL in bypass mode. DoEnable()305 Status DoEnable() override { 306 // If `config_` is specified, the PLL should be enabled and the phase 307 // fractional divider PFD0 needs to get configured, otherwise the PLL 308 // operates in bypass mode. 309 if (config_ != nullptr) { 310 // Configure Audio PLL clock source. 311 CLOCK_InitAudioPll(config_); 312 CLOCK_InitAudioPfd(kCLOCK_Pfd0, audio_pfd_divider_); 313 } else { 314 // PLL operates in bypass mode. 315 CLKCTL1->AUDIOPLL0CLKSEL = bypass_source_; 316 CLKCTL1->AUDIOPLL0CTL0 |= CLKCTL1_AUDIOPLL0CTL0_BYPASS_MASK; 317 } 318 return OkStatus(); 319 } 320 321 /// Disables the audio PLL logic. DoDisable()322 Status DoDisable() override { 323 if (config_ != nullptr) { 324 // Clock gate the phase fractional divider PFD0. 325 CLOCK_DeinitAudioPfd(kCLOCK_Pfd0); 326 } 327 328 // Power down Audio PLL 329 CLOCK_DeinitAudioPll(); 330 331 // Clock gate audio PLL clock selector. 332 CLKCTL1->AUDIOPLL0CLKSEL = kCLOCK_AudioPllNone; 333 return OkStatus(); 334 } 335 336 /// Optional audio PLL configuration. 337 const clock_audio_pll_config_t* config_ = nullptr; 338 339 /// Optional audio kCLOCK_Pfd0 clock divider value. 340 const uint8_t audio_pfd_divider_ = 0; 341 342 /// Optional audio PLL bypass clock source. 343 const audio_pll_src_t bypass_source_ = kCLOCK_AudioPllNone; 344 }; 345 346 /// Alias for a blocking audio PLL clock tree element. 347 using ClockMcuxpressoAudioPllBlocking = 348 ClockMcuxpressoAudioPll<ElementBlocking>; 349 350 /// Alias for a non-blocking audio PLL clock tree element where updates 351 /// cannot fail. 352 using ClockMcuxpressoAudioPllNonBlocking = 353 ClockMcuxpressoAudioPll<ElementNonBlockingCannotFail>; 354 355 } // namespace pw::clock_tree 356