Skip to content

File clock.hpp

File List > include > libhal-lpc40 > clock.hpp

Go to the documentation of this file

// Copyright 2023 Google LLC
//
// 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
//
//      http://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.

#pragma once

#include <array>
#include <cstdint>

#include <libhal-util/bit.hpp>
#include <libhal-util/enum.hpp>
#include <libhal/error.hpp>
#include <libhal/units.hpp>

#include "constants.hpp"

namespace hal::lpc40 {
class clock
{
public:
  static constexpr hertz irc_frequency = 12'000'000.0f;
  static constexpr uint32_t default_peripheral_divider = 4;

  enum class usb_clock_source : uint8_t
  {
    system_clock = 0b00,
    pll0 = 0b01,
    pll1 = 0b10,
  };

  enum class usb_divider : uint8_t
  {
    divide_by1 = 0,
    divide_by2,
    divide_by3,
    divide_by4,
  };

  enum class spifi_clock_source : uint8_t
  {
    system_clock = 0b00,
    pll0 = 0b01,
    pll1 = 0b10,
  };

  enum class flash_configuration : uint32_t
  {
    clock1 = 0b0000 << 12,
    clock2 = 0b0001 << 12,
    clock3 = 0b0010 << 12,
    clock4 = 0b0011 << 12,
    clock5 = 0b0100 << 12,
    clock6 = 0b0101 << 12,
  };

  struct configuration
  {
    hertz oscillator_frequency = irc_frequency;
    bool use_external_oscillator = false;
    struct pll_t
    {
      bool enabled = false;
      uint8_t multiply = 1;
    };
    std::array<pll_t, 2> pll = {};
    struct cpu_t
    {
      bool use_pll0 = false;
      uint8_t divider = 1;
    };
    cpu_t cpu = {};

    struct usb_t
    {
      usb_clock_source clock = usb_clock_source::system_clock;
      usb_divider divider = usb_divider::divide_by1;
    };
    usb_t usb = {};

    struct spifi_t
    {
      spifi_clock_source clock = spifi_clock_source::system_clock;
      uint8_t divider = 1;
    };
    spifi_t spifi = {};

    uint8_t peripheral_divider = 4;

    bool emc_half_cpu_divider = false;
  };

  static clock& get();

  static status maximum(hertz p_external_crystal_frequency);

  hertz get_frequency(peripheral p_peripheral);

  configuration& config();

  [[nodiscard]] status reconfigure_clocks();

private:
  constexpr clock()
  {
  }

  configuration m_config{};
  hertz m_cpu_clock_rate = irc_frequency;
  hertz m_emc_clock_rate = irc_frequency;
  hertz m_usb_clock_rate = irc_frequency;
  hertz m_spifi_clock_source_rate = irc_frequency;
  hertz m_peripheral_clock_rate = irc_frequency / default_peripheral_divider;
};
}  // namespace hal::lpc40