1 /*
2  * Copyright (c) 2022-2023, Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/logging/log.h>
9 #include <socfpga_system_manager.h>
10 
11 #include "clock_control_agilex5_ll.h"
12 
13 LOG_MODULE_REGISTER(clock_control_agilex5_ll, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
14 
15 /* Clock manager individual group base addresses. */
16 struct clock_agilex5_ll_params {
17 	mm_reg_t base_addr;
18 	mm_reg_t mainpll_addr;
19 	mm_reg_t peripll_addr;
20 	mm_reg_t ctl_addr;
21 };
22 
23 /* Clock manager low layer(ll) params object. */
24 static struct clock_agilex5_ll_params clock_agilex5_ll;
25 
26 /* Initialize the clock ll with the given base address */
clock_agilex5_ll_init(mm_reg_t base_addr)27 void clock_agilex5_ll_init(mm_reg_t base_addr)
28 {
29 	/* Clock manager module base address. */
30 	clock_agilex5_ll.base_addr = base_addr;
31 
32 	/* Clock manager main PLL base address. */
33 	clock_agilex5_ll.mainpll_addr = clock_agilex5_ll.base_addr + CLKMGR_MAINPLL_OFFSET;
34 
35 	/* Clock manager peripheral PLL base address. */
36 	clock_agilex5_ll.peripll_addr = clock_agilex5_ll.base_addr + CLKMGR_PERPLL_OFFSET;
37 
38 	/* Clock manager control module base address. */
39 	clock_agilex5_ll.ctl_addr = clock_agilex5_ll.base_addr + CLKMGR_INTEL_OFFSET;
40 }
41 
42 /* Extract reference clock from platform clock source */
get_ref_clk(uint32_t pllglob)43 static uint32_t get_ref_clk(uint32_t pllglob)
44 {
45 	uint32_t arefclkdiv, ref_clk;
46 	uint32_t scr_reg;
47 
48 	/*
49 	 * Based on the clock source, read the values from System Manager boot
50 	 * scratch registers. These values are filled by boot loader based on
51 	 * hand-off data.
52 	 */
53 	switch (CLKMGR_PSRC(pllglob)) {
54 	case CLKMGR_PLLGLOB_PSRC_EOSC1:
55 		scr_reg = SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1);
56 		ref_clk = sys_read32(scr_reg);
57 		break;
58 
59 	case CLKMGR_PLLGLOB_PSRC_INTOSC:
60 		ref_clk = CLKMGR_INTOSC_HZ;
61 		break;
62 
63 	case CLKMGR_PLLGLOB_PSRC_F2S:
64 		scr_reg = SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2);
65 		ref_clk = sys_read32(scr_reg);
66 		break;
67 
68 	default:
69 		ref_clk = 0;
70 		LOG_ERR("Invalid VCO input clock source");
71 		break;
72 	}
73 
74 	/* Reference clock divider, to get the effective reference clock. */
75 	arefclkdiv = CLKMGR_PLLGLOB_AREFCLKDIV(pllglob);
76 	ref_clk /= arefclkdiv;
77 
78 	return ref_clk;
79 }
80 
81 /* Calculate clock frequency based on parameter */
get_clk_freq(uint32_t psrc_reg,uint32_t main_pllc,uint32_t per_pllc)82 static uint32_t get_clk_freq(uint32_t psrc_reg, uint32_t main_pllc, uint32_t per_pllc)
83 {
84 	uint32_t clk_psrc, mdiv, ref_clk;
85 	uint32_t pllm_reg, pllc_reg, pllc_div, pllglob_reg;
86 
87 	clk_psrc = sys_read32(clock_agilex5_ll.mainpll_addr + psrc_reg);
88 
89 	switch (CLKMGR_PSRC(clk_psrc)) {
90 	case CLKMGR_PSRC_MAIN:
91 		pllm_reg = clock_agilex5_ll.mainpll_addr + CLKMGR_MAINPLL_PLLM;
92 		pllc_reg = clock_agilex5_ll.mainpll_addr + main_pllc;
93 		pllglob_reg = clock_agilex5_ll.mainpll_addr + CLKMGR_MAINPLL_PLLGLOB;
94 		break;
95 
96 	case CLKMGR_PSRC_PER:
97 		pllm_reg = clock_agilex5_ll.peripll_addr + CLKMGR_PERPLL_PLLM;
98 		pllc_reg = clock_agilex5_ll.peripll_addr + per_pllc;
99 		pllglob_reg = clock_agilex5_ll.peripll_addr + CLKMGR_PERPLL_PLLGLOB;
100 		break;
101 
102 	default:
103 		return 0;
104 	}
105 
106 	ref_clk = get_ref_clk(sys_read32(pllglob_reg));
107 	mdiv = CLKMGR_PLLM_MDIV(sys_read32(pllm_reg));
108 	ref_clk *= mdiv;
109 
110 	/* Clock slice divider ration in binary code. */
111 	pllc_div = CLKMGR_PLLC_DIV(sys_read32(pllc_reg));
112 
113 	return ref_clk / pllc_div;
114 }
115 
116 /* Return L3 interconnect clock */
get_l3_clk(void)117 uint32_t get_l3_clk(void)
118 {
119 	uint32_t l3_clk;
120 
121 	l3_clk = get_clk_freq(CLKMGR_MAINPLL_NOCCLK, CLKMGR_MAINPLL_PLLC1, CLKMGR_PERPLL_PLLC1);
122 
123 	return l3_clk;
124 }
125 
126 /* Calculate clock frequency to be used for mpu */
get_mpu_clk(void)127 uint32_t get_mpu_clk(void)
128 {
129 	uint32_t mpu_clk;
130 
131 	mpu_clk = get_clk_freq(CLKMGR_MAINPLL_MPUCLK, CLKMGR_MAINPLL_PLLC0, CLKMGR_PERPLL_PLLC0);
132 
133 	return mpu_clk;
134 }
135 
136 /* Calculate clock frequency to be used for watchdog timer */
get_wdt_clk(void)137 uint32_t get_wdt_clk(void)
138 {
139 	uint32_t l4_sys_clk;
140 
141 	l4_sys_clk = (get_l3_clk() >> 2);
142 
143 	return l4_sys_clk;
144 }
145 
146 /* Calculate clock frequency to be used for UART driver */
get_uart_clk(void)147 uint32_t get_uart_clk(void)
148 {
149 	uint32_t mainpll_nocdiv, l4_sp_clk;
150 
151 	mainpll_nocdiv = sys_read32(clock_agilex5_ll.mainpll_addr + CLKMGR_MAINPLL_NOCDIV);
152 	mainpll_nocdiv = CLKMGR_MAINPLL_L4SPDIV(mainpll_nocdiv);
153 
154 	l4_sp_clk = (get_l3_clk() >> mainpll_nocdiv);
155 
156 	return l4_sp_clk;
157 }
158 
159 /* Calculate clock frequency to be used for SDMMC driver */
get_mmc_clk(void)160 uint32_t get_mmc_clk(void)
161 {
162 	uint32_t sdmmc_ctr, mmc_clk;
163 
164 	mmc_clk = get_clk_freq(CLKMGR_INTEL_SDMMCCTR, CLKMGR_MAINPLL_PLLC3, CLKMGR_PERPLL_PLLC3);
165 
166 	sdmmc_ctr = sys_read32(clock_agilex5_ll.ctl_addr + CLKMGR_INTEL_SDMMCCTR);
167 	sdmmc_ctr = CLKMGR_INTEL_SDMMC_CNT(sdmmc_ctr);
168 	mmc_clk = ((mmc_clk / sdmmc_ctr) >> 2);
169 
170 	return mmc_clk;
171 }
172 
173 /* Calculate clock frequency to be used for Timer driver */
get_timer_clk(void)174 uint32_t get_timer_clk(void)
175 {
176 	uint32_t l4_sys_clk;
177 
178 	l4_sys_clk = (get_l3_clk() >> 2);
179 
180 	return l4_sys_clk;
181 }
182