#ifndef _PLATFORM_CLOCK_H
#define _PLATFORM_CLOCK_H


#include <stdint.h>
#include <macro.h>

/* Clocking and reset is managed by the PRCI (Power Reset Clocking Interrupt)
 * block (Figure 24) [77]
 *
 * FU740-C000 generates all internal clocks from 26 MHz hfclk driven from an
 * external oscillator (HFCLKIN) or crystal (HFOSCIN) input, selected by input
 * HFXSEL. All harts operate in a single clock domain (coreclk) supplied by
 * either corepll or dvfscorepll, which can be selected using the corepllsel
 * register. These PLLs step 26 MHz hfclk up to higher frequencies.
 * The recommended frequency of coreclk is 1.0GHz, however operation at up to
 * 1.5GHz is possible.
 * tlclk is a divided version of the coreclk and generates the clock for the L2
 * cache.
 * The hfpclkpll generates the clock for peripherals such as SPI, UART, GPIO,
 * I2C, and PWM.
 * dvfs_core_pll enables the user to change the CPU frequency without dropping
 * down to the lower frequency hfclk.
 * The DDR, Ethernet and PCIe Subsystems operate asynchronously.
 * The PRCI contains two dedicated PLLs used to step 26 MHz hfclk up to the DDR
 * and Ethernet operating frequencies.
 * The PCIe Subsystem contains its own clock generation.
 * The PRCI contains memory-mapped registers that control the clock selection
 * and configuration of the PLLs.
 * On power-on, the default PRCI register settings start the harts running
 * directly from hfclk.
 * All additional clock management, for instance initializing the DDR PLL or
 * stepping the coreclk frequency, is performed through software reads and
 * writes to the memory-mapped PRCI control registers.
 * The CPU real time clock (rtcclk) runs at 1 MHz and is driven from input pin
 * RTCCLKIN. This should be connected to an external oscillator.
 * JTAG debug logic runs off of JTAG TCK as described in Chapter 26.
 *
 * Reset
 * The FU740-C000 has two external reset pins.
 * PORESET_N is an asynchonous active low power-on reset that should be
 * connected to an external power sequencing/supervisory circuit. ERESET_N is
 * an asynchonous active low reset that can be connected to a reset button.
 * There is internal debounce and stretch logic. The PRCI also contains hardware
 * to generate internal synchronous resets for coreclk, tlclk, and hfpclk
 * domains and handle reset to and from the debug module. Resets for the DDR,
 * Eth- ernet and PCIE Subsystems are performed through software reads and
 * writes to memory-mapped PRCI control registers. These registers are outlined
 * in Table 34 below.
 */

/*	Table 21: PRCI Memory Map
 * Offset 	Name Description
 * 0x00    hfxosccfg Crystal Oscillator Configuration and Status
 * 0x04    core_pllcfg PLL Configuration and Status
 * 0x08    core_plloutdiv PLL Final Divide Configuration
 * 0x0C    ddr_pllcfg PLL Configuration and Status
 * 0x10    ddr_plloutdiv PLL Final Divide Configuration
 * 0x1C    gemgxl_pllcfg PLL Configuration and Status
 * 0x20    gemgxl_plloutdiv PLL Final Divide Configuration
 * 0x24    core_clk_sel_reg Select core clock source. 0: coreclkpll 1: external hfclk
 * 0x28    devices_reset_n Software controlled resets (active low)
 * 0x2C    clk_mux_status Current selection of each clock mux
 * 0x38    dvfs_core_pllcfg PLL Configuration and Status
 * 0x3C    dvfs_core_plloutdiv PLL Final Divide Configuration
 * 0x40    corepllsel Select which PLL output to use for core clock. 0: corepll 1: dvfscorepll
 * 0x50    hfpclk_pllcfg PLL Configuration and Status
 * 0x54    hfpclk_plloutdiv PLL Final Divide Configuration
 * 0x58    hfpclkpllsel Select source for Periphery Clock (pclk). 0: hfpclkpll 1: external hfclk
 * 0x5C    hfpclk_div_reg HFPCLK PLL divider value
 * 0xE0    prci_plls Indicates presence of each PLL
 */

#define HFCLK_FREQ_HZ 26000000L
#define PRCI_MEMORY_BLOCK 0x10000000L


struct S_PACKED prci_pllcfg {
	uint32_t pllr:6;         // [RW, 0x1] PLL R Value
	uint32_t pllf:9;         // [RW, 0x1F] PLL F Value
	uint32_t pllq:3;         // [RW, 0x3] PLL Q Value
	uint32_t pllrange:3;     // [RW, 0x0] PLL Range Value
	uint32_t reserved0:3;
	uint32_t pllbypass:1;    // [RW, 0x1] PLL Bypass
	uint32_t pllfsebypass:1; // [RW, 0x1] PLL FSE Bypass
	uint32_t reserved1:5;
	uint32_t plllock:1;      // [RO, X] PLL Lock
};


struct S_PACKED prci_plloutdiv {
	uint32_t reserved:31;
	uint32_t pllcke:1; // [RW, 0x0] PLL Clock Enable
};

struct S_PACKED prci_mem_map {
	// Offset 0x0
	struct prci_hfxosccfg {
		uint32_t reserved:30;
		uint32_t hfxoscen:1;  // [RW, 0x1] Crystal Oscillator Enable
		uint32_t hfxoscrdy:1; // [RO, X] Crystal Oscillator Ready
	} hfxosccfg;

	// Offset 0x4
	struct prci_pllcfg core_pllcfg;

	// Offset 0x8
	struct { uint32_t reserved; } core_plloutdiv;

	// Offset 0xC
	struct prci_pllcfg ddr_pllcfg;

	// Offset 0x10
	struct prci_plloutdiv ddr_plloutdiv;
	uint8_t off0[8];

	// Offset 0x1C
	struct prci_pllcfg gemgxl_pllcfg;

	// Offset 0x20
	struct prci_plloutdiv gemgxl_plloutdiv;

	// Offset 0x24
	uint32_t core_clk_sel_reg;

	// Offset 0x28
	struct prci_devices_reset_n {
		uint32_t ddrctrl_reset_n:1; // [RW, 0x0] Active-Low ddrctrl reset
		uint32_t ddraxi_reset_n:1;  // [RW, 0x0] Active-Low ddraxi reset
		uint32_t ddrahb_reset_n:1;  // [RW, 0x0] Active-Low ddrahb reset
		uint32_t ddrphy_reset_n:1;  // [RW, 0x0] Active-Low ddrphy reset
		uint32_t pcieaux_reset_n:1; // [RW, 0x0] Active-Low pcieaux reset
		uint32_t gemgxl_reset_n:1;  // [RW, 0x0] Active-Low gemgxl reset
		uint32_t reserved:26;

	} devices_reset_n;

	// Offset 0x2C
	struct prci_clk_mux_status {
		uint32_t coreclkpllsel:1; // [RO, X] Current setting of coreclkpllsel mux
		uint32_t tlclksel:1;      // [RO, X] Current setting of tlclksel mux
		uint32_t rtcxsel:1;       // [RO, X] Current setting of rtcxsel mux
		uint32_t ddrctrlclksel:1; // [RO, X] Current setting of ddrctrlclksel mux
		uint32_t ddrphyclksel:1;  // [RO, X] Current setting of ddrphyclksel mux
		uint32_t reserved0:1;     // [RO, X] Current setting of reserved0 mux
		uint32_t gemgxlclksel:1;  // [RO, X] Current setting of gemgxlclksel mux
		uint32_t mainmemclksel:1; // [RO, X] Current setting of mainmemclksel mux
		uint32_t reserved:24;
	} clk_mux_status;
	uint8_t off1[8];

	// Offset 0x38
	struct prci_pllcfg dvfs_core_pllcfg;

	// Offset 0x3C
	struct prci_plloutdiv dvfs_core_plloutdiv;

	// Offset 0x40
	uint32_t corepllsel;
	uint8_t off2[12];

	// Offset 0x50
	struct prci_pllcfg hfpclk_pllcfg;

	// Offset 0x54
	struct prci_plloutdiv hfpclk_plloutdiv;

	// Offset 0x58
	uint32_t hfpclkpllsel;

	// Offset 0x5C
	uint32_t hfpclk_div_reg; // [RW, 0x0] HFPCLK PLL divider value
	uint8_t off3[128];

	// Offset 0xE0
	struct prci_prci_plls {
		uint32_t cltxpll:1;     // [RO, X] Indicates presence of cltxpll
		uint32_t gemgxlpll:1;   // [RO, X] Indicates presence of gemgxlpll
		uint32_t ddrpll:1;      // [RO, X] Indicates presence of ddrpll
		uint32_t hfpclkpll:1;   // [RO, X] Indicates presence of hfpclkpll
		uint32_t dvfscorepll:1; // [RO, X] Indicates presence of dvfscorepll
		uint32_t corepll:1;     // [RO, X] Indicates presence of corepll
		uint32_t reserved:26;
	} prci_plls;
};
static_assert((sizeof(struct prci_mem_map) == 0xE4),
              "prci_mem_map is not the right size");


#endif