// Copyright 2024 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

#include "pw_clock_tree_mcuxpresso/clock_tree.h"

// Test headers
#include "pw_unit_test/framework.h"

// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-Flexcomm0]

// Define FRO_DIV_4 clock source
PW_CONSTINIT pw::clock_tree::ClockMcuxpressoFro fro_div_4(kCLOCK_FroDiv4OutEn);

// Define FRG0 configuration
const clock_frg_clk_config_t g_frg0Config_BOARD_BOOTCLOCKRUN = {
    .num = 0,
    .sfg_clock_src = _clock_frg_clk_config::kCLOCK_FrgFroDiv4,
    .divider = 255U,
    .mult = 144};

PW_CONSTINIT pw::clock_tree::ClockMcuxpressoFrgNonBlocking frg_0(
    fro_div_4, g_frg0Config_BOARD_BOOTCLOCKRUN);

// Define clock source selector FLEXCOMM0
PW_CONSTINIT pw::clock_tree::ClockMcuxpressoSelectorNonBlocking flexcomm_0(
    frg_0, kFRG_to_FLEXCOMM0, kNONE_to_FLEXCOMM0);

// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-Flexcomm0]

// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-i3c0]

// Define FRO_DIV_8 clock source
PW_CONSTINIT pw::clock_tree::ClockMcuxpressoFro fro_div_8(kCLOCK_FroDiv8OutEn);

// Define clock source selector I3C01FCLKSEL
PW_CONSTINIT pw::clock_tree::ClockMcuxpressoSelectorNonBlocking i3c0_selector(
    fro_div_8, kFRO_DIV8_to_I3C_CLK, kNONE_to_I3C_CLK);

// Define clock divider I3C01FCLKDIV
PW_CONSTINIT pw::clock_tree::ClockMcuxpressoDividerNonBlocking i3c0_divider(
    i3c0_selector, kCLOCK_DivI3cClk, 12);

// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-i3c0]

// DOCSTAG:
// [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-ClockSourceNoOp]

// Need to define `ClockSourceNoOp` clock tree element to satisfy dependency for
// `ClockMcuxpressoMclk` or `ClockMcuxpressoClkIn` class.
PW_CONSTINIT pw::clock_tree::ClockSourceNoOp clock_source_no_op;

// DOCSTAG:
// [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-ClockSourceNoOp]

// inclusive-language: disable
// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-Ctimer0]

// Define Master clock
PW_CONSTINIT pw::clock_tree::ClockMcuxpressoMclkNonBlocking mclk(
    clock_source_no_op, 19200000);

// Define clock selector CTIMER0
PW_CONSTINIT pw::clock_tree::ClockMcuxpressoSelectorNonBlocking ctimer_0(
    mclk, kMASTER_CLK_to_CTIMER0, kNONE_to_CTIMER0);

// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-Ctimer0]
// inclusive-language: enable

// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-LpOsc]

// Define Low-Power Oscillator
PW_CONSTINIT pw::clock_tree::ClockMcuxpressoLpOsc lp_osc_clk;

// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-LpOsc]

// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-AudioPll]

// Define ClkIn pin clock source
PW_CONSTINIT pw::clock_tree::ClockMcuxpressoClkInNonBlocking clk_in(
    clock_source_no_op, 19200000);

// Define audio PLL configuration with ClkIn pin as clock source
const clock_audio_pll_config_t kAudioPllConfig = {
    .audio_pll_src = kCLOCK_AudioPllXtalIn, /* OSC clock */
    .numerator =
        0, /* Numerator of the Audio PLL fractional loop divider is 0 */
    .denominator =
        1000, /* Denominator of the Audio PLL fractional loop divider is 1 */
    .audio_pll_mult = kCLOCK_AudioPllMult16 /* Divide by 16 */
};

// Define Audio PLL sourced by ClkIn pin clock source
PW_CONSTINIT pw::clock_tree::ClockMcuxpressoAudioPllNonBlocking audio_pll(
    clk_in, kAudioPllConfig, 18);

// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-AudioPll]

// DOCSTAG:
// [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-AudioPllBypass]

// Define Audio PLL in bypass mode sourced by FRO_DIV_8 clock source
PW_CONSTINIT pw::clock_tree::ClockMcuxpressoAudioPllNonBlocking
    audio_pll_bypass(fro_div_8, kCLOCK_AudioPllFroDiv8Clk);

// DOCSTAG:
// [pw_clock_tree_mcuxpresso-examples-ClockTreeElementDefs-AudioPllBypass]

// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeDef]

// Create the clock tree
pw::clock_tree::ClockTree clock_tree;

// DOCSTAG: [pw_clock_tree_mcuxpresso-examples-ClockTreeDef]

TEST(ClockTreeMcuxpresso, UseExample) {
  // DOCSTAG: [pw_clock_tree_mcuxpresso-examples-UseExample]

  // Enable the low-power oscillator
  clock_tree.Acquire(lp_osc_clk);

  // Enable the i3c0_divider
  clock_tree.Acquire(i3c0_divider);

  // Change the i3c0_divider value
  clock_tree.SetDividerValue(i3c0_divider, 24);

  // Disable the low-power oscillator
  clock_tree.Release(lp_osc_clk);

  // DOCSTAG: [pw_clock_tree_mcuxpresso-examples-UseExample]
}

TEST(ClockTreeMcuxpresso, AudioPll) {
  clock_tree.Acquire(audio_pll);
  clock_tree.Release(audio_pll);
}

TEST(ClockTreeMcuxpresso, AudioPllBypass) {
  clock_tree.Acquire(audio_pll_bypass);
  clock_tree.Release(audio_pll_bypass);
}
