/*******************************************************************************
 * Copyright 2019-2022 Microchip FPGA Embedded Systems Solutions.
 *
 * SPDX-License-Identifier: MIT
 *
 * MPFS HAL Embedded Software
 *
 */

/*******************************************************************************
 * @file mss_pll.h
 * @author Microchip-FPGA Embedded Systems Solutions
 * @brief PLL defines
 *
 */

/*=========================================================================*//**
  @page PolarFire SoC MSS Clock Setup
  ==============================================================================
  Introduction
  ==============================================================================
  The PolarFire SoC Microprocessor subsystem (MSS) has three PLL's, the MSS, DDR
  and MSS SGMII's.
  Two CFM IP blocks are used to mux the PLL input and outputs.

  ==============================================================================
  PLL Inputs
  ==============================================================================
  Each of the three PLL's can be configured with the following inputs:
    - VSS ( default on reset )
    - external ref clock in SGMII IO Block
    - SCB Clock (80MHz)
    - North West corner clock mux structure ICB

  There are two bits associated with setting clk source

  Note on North West corner clock mux structure ICB
  The Fabric reference clock inputs source come from the clock mux's in the
  upper  left corner (regular FPGA corner) that provided clocks that can be
  routed in from various places that will include from IOs, from the FPGA
  fabric, etc.



  ==============================================================================
  MSS PLL Outputs
  ==============================================================================
  Each PLL has four outputs. These are generally gated through a mux
  MSS PLL Outputs

    | Output(0-3)   |    Detail     | Mux           | Muxed with  |
    | ------------- |:-------------:|:-------------:| -----------:|
    | msspll_fdr_0  | clk_in_mss    | no            | -           |
    | msspll_fdr_1  | clk_in_crypto | no            | -           |
    | msspll_fdr_2  | clk_in_emmc   | glitch-less   | SCB clk     |
    | msspll_fdr_3  | clk_in_can    | glitch-less   | SCB clk     |

  ==============================================================================
  SCB bus timing
  ==============================================================================
  When the MSS is using the SCB bus, there is a timing relationship.
  The defaults should be OK here. In necessary, the timing used by the MSS SCB
  access is adjusted using the MSS system register TIMER.
  (SCBCFG_REGS->TIMER.TIMER)
      - Bits 15:8 Sets how long SCB request is held active after SCB bus granted.
      - Allows SCB bus master-ship to maintained across multiple SCB access
        cycles
      - Bits 7:0 Set the timeout for an SCB access in CPU cycles.

  ==============================================================================
  eNVM timing
  ==============================================================================
  The clk used by the eNVM is adjusted using the following register:
  SYSREG->ENVM_CR
     [5:0]
     Sets the number of AHB cycles used to generate the PNVM clock,.
     Clock period = (Value+1) * (1000/AHBFREQMHZ)
     Value must be 1 to 63 (0 defaults to 15)
     e.g.
     11 will generate a 40ns period 25MHz clock if the AHB clock is 250MHz
     15 will generate a 40ns period 25MHz clock if the AHB clock is 400MHz

  ==============================================================================
  MSS clocks at reset
  ==============================================================================
  When the MSS comes out of reset, the MSS will be running on the SCB supplied
  80MHz clock.



  ==============================================================================
  MSS clock setup - Use case one - using external reference from SGMII I/O Blk
  ==============================================================================
  Use case one will be used in the majority of cases.
  This is where the MSS PLL reference clk will be supplied from an external
  reference through the external ref clock in SGMII IO Block.


                                                 scb clk
                     01=>ext clk ref             +
                      +----------+   +--------+  |  0=>SCB
                  crn |          |   |  MSS   |  |  1=>PLL
                 +--->|          |   |  PLL   |  | +------+
     +-----+      scb |          |   |        |  +>|      |mss
     |     |     +----           |   |        |    | mux  +-->
     | ext + p        |  mux     +-->|ref0   0+--->|      |clk
     | clk +--------->|          |   |        |    +------+
     | ref | n    vss |          |   |        |     0=>SCB
     |     +---+    ->|          |   |        |     1=>PLL
     |     |   |      |          |   |        |    +------+
     |     |   |      |          |   |        |scb-|      |crypto
     +-----+   |      +----------+   |        |    | mux  +-->
               |                     |       1+--->|      |clk
               |    01=>ext clk ref  |        |    +------+
               |      +----------+   |        |
               |   crn|          |   |        |    +------+
               |  +-->|          |   |        |    |      |
               |   scb|          |   |       2+--->| eMMC +
               |  +-->|          |   |        |    |      |
               |      |  mux     +-->|ref1    |    +------+
               +----->|          |   |        |
                   vss|          |   |        |    +------+
                  +-->|          |   |        |    |      |
                      |          |   |       3+--->| CAN  +
                      |          |   |        |    |      |
                      +----------+   +--------+    +------+

  Steps to setup ext clk:
  1. The external clock reference is setup- In SGMII setup
  2. The input mux is set to take input from ext ref clk
  3. MSS PLL clock is setup with required settings
  4. PLL is checked until locked
  5. MSS PLL output is switched through to MSS ( switch code run from ram )
  6. eNVM clcock is changed as required
  7. return from RAM routine and continue

  ==============================================================================
  MSS clock dividers
  ==============================================================================
  The three dividers generating the MSS master clocks are controllable via the
  system register (CLOCK_CONFIG_CR).
  CPU clock dividers are set in the function  mss_mux_post_mss_pll_config(void)

    | Divider       | Config bits   | Reset      | MAX feq. *  |
    | ------------- |:-------------:|:----------:| -----------:|
    | CPU           | 1:0           | 00         | 625         |
    | AXI           | 3:2           | 01         | 312.5       |
    | AHB/APB       | 5:4           | 10         | 156.25      |

  settings = 00=/1, 01=/2, 01=/4, 01=/8
  * verify MAX feq. setting with particular silicon variant data sheet
   - The CPU clock must not exceed 625MHz
   - The AXI clock  must not exceed 312.5MHz
   - The AHB clock must not exceed 156.25MHz
   - The CPU clock must be greater or equal to the AXI clock
   - The AHB/APB clocks cannot be divided by 1. Divide by 1 will divide the
     clock by 2.
   - The clock divider register may be changed from any value to any value in
     one go.
   - When the USB block is in-use the AHB clock must be greater than 66 MHz.


                          +---------------------+          +-------------+
                          |      +----------+   |          |             |
                          |   +--> /1/2/4/8 +-->+--------->|   CPU cores |
    +-----------+         |   |  +----------+   |          |             |
    |           |         |   |                 |          +-------------+
    |  MSS CLK  |         |   |                 |
    |           |         |   |                 |          +-------------+
    |           +---------+---+  +---------+    |          | dfi_apb_pclk|
    |           |         |   +->|/4/8/16  +--->+---------->             |
    |           |         |   |  +---------+    |          |             |
    |           |         |   |                 |          +-------------+
    +-----------+         |   |                 |
                          |   |                 |
                          |   |                 |          +-------------+
                          |   |  +---------+    |          | MSS AXI     |
                          |   +--> 1/2/4/8 +--->+--------->| Buses and   |
                          |   |  +---------+    |          | peripherals |
                          |   |                 |          +-------------+
                          |   |                 |
                          |   |                 |
                          |   |                 |          +-------------+
                          |   |  +-------+      |          | MSS APB/AHB |
                          |   +->| 2/4/8 +----->+--------->| Buses and
                          |      +-------+      |          | peripherals |
                          +---------------------+          +-------------+


 *//*=========================================================================*/
#ifndef MSS_DDR_SGMII_MSS_PLL_H_
#define MSS_DDR_SGMII_MSS_PLL_H_

#ifdef __cplusplus
extern "C" {
#endif

#define PLL_CTRL_LOCK_BIT ((0x01U) << 25U)
/*
 * bit0 1: This when asserted resets all the non-volatile register  bits
 *         e.g. RW-P bits, the bit self clears i.e. is similar to a W1P bit
 * bit1 1: This when asserted resets all the register  bits apart from the
 *         non-volatile registers,  the bit self clears.  i.e. is similar to a
 *         W1P bit
 */
#define PLL_INIT_AND_OUT_OF_RESET               0x00000003UL

#define PLL_CTRL_REG_POWERDOWN_B_MASK           0x00000001UL

typedef enum RTC_CLK_SOURCE_
{
    SCB_80M_CLOCK                   = 0x00,       /*!< 0 SCB clock source */
    MSS_PLL_CLOCK                   = 0x01,       /*!< 1 MSS PLL clock source */
}   RTC_CLK_SOURCE;

typedef enum REG_LOAD_METHOD_
{
    SCB_UPDATE                   = 0x00,       /*!< 0 SCB direct load */
    RPC_REG_UPDATE               = 0x01,       /*!< 1 RPC -> SCB load */
}   REG_LOAD_METHOD;



/***************************************************************************//**
  ddr_pll_config() configure DDR PLL

  Example:
  @code

      ddr_pll_config();

  @endcode

 */
void ddr_pll_config(REG_LOAD_METHOD option);

/***************************************************************************//**
  ddr_pll_lock_scb() Checks if PLL locked

  @return
    0U if locked

  Example:
  @code
      if (ddr_pvt_calibration() == 0U)
      {
           PLL is locked
      }
  @endcode

 */
uint8_t ddr_pll_lock_scb(void);

/***************************************************************************//**
  sgmii_pll_config_scb() configure sgmii PLL

   @param option 1 => soft reset, load RPC settings
                 0 => write values using SCB

  Example:
  @code

      sgmii_pll_config_scb(1U);

  @endcode

 */
void sgmii_pll_config_scb(uint8_t option);

/***************************************************************************//**
  sgmii_pll_lock_scb() Checks if PLL is locked

   @return
    0U if locked

  Example:
  @code
      if (ddr_pvt_calibration() == 0U)
      {
           PLL is locked
      }
  @endcode

 */
uint8_t sgmii_pll_lock_scb(void);

/***************************************************************************//**
  ddr_pll_config_scb_turn_off() Puts PLL in reset

  Example:
  @code

      ddr_pll_config_scb_turn_off();

  @endcode

 */
void ddr_pll_config_scb_turn_off(void);

/***************************************************************************//**
  set_RTC_divisor() Sets the RTC divisor based on values from Libero
  It is assumed the RTC clock is set to 1MHz
  Example:
  @code

      set_RTC_divisor();

  @endcode

 */
void set_RTC_divisor(void);

/***************************************************************************//**
  sgmii_mux_config_via_scb() configures mux for SGMii

  Example:
  @code

      sgmii_mux_config_via_scb();

  @endcode

 */
void sgmii_mux_config_via_scb(uint8_t option);

/***************************************************************************//**
  pre_configure_sgmii_and_ddr_pll_via_scb()

  @param option 1 => soft reset, load RPC settings
                0 => write values using SCB

  Example:
  @code

      ddr_pvt_calibration(1U);

  @endcode

 */
void pre_configure_sgmii_and_ddr_pll_via_scb(uint8_t option);

/***************************************************************************//**
  mss_pll_config()

  Setup the MSS PLL
  feeds in ref clock, set freq based on MSS Configurator settings and once
  lock achieved, feeds out to the MSS.

  Example:
  @code

      mss_pll_config();

  @endcode

 */
void mss_pll_config(void);

/***************************************************************************//**
  flag_mss_pll_lock_error()

  Instantiate platform specific function to give error feedback on your platform

 */
void flag_mss_pll_lock_error(void);

#ifdef __cplusplus
}
#endif

#endif /* MSS_DDR_SGMII_MSS_PLL_H_ */
