/* * Copyright (c) 2022-2023, Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include "clock_control_agilex5_ll.h" LOG_MODULE_REGISTER(clock_control_agilex5_ll, CONFIG_CLOCK_CONTROL_LOG_LEVEL); /* Clock manager individual group base addresses. */ struct clock_agilex5_ll_params { mm_reg_t base_addr; mm_reg_t mainpll_addr; mm_reg_t peripll_addr; mm_reg_t ctl_addr; }; /* Clock manager low layer(ll) params object. */ static struct clock_agilex5_ll_params clock_agilex5_ll; /* Initialize the clock ll with the given base address */ void clock_agilex5_ll_init(mm_reg_t base_addr) { /* Clock manager module base address. */ clock_agilex5_ll.base_addr = base_addr; /* Clock manager main PLL base address. */ clock_agilex5_ll.mainpll_addr = clock_agilex5_ll.base_addr + CLKMGR_MAINPLL_OFFSET; /* Clock manager peripheral PLL base address. */ clock_agilex5_ll.peripll_addr = clock_agilex5_ll.base_addr + CLKMGR_PERPLL_OFFSET; /* Clock manager control module base address. */ clock_agilex5_ll.ctl_addr = clock_agilex5_ll.base_addr + CLKMGR_INTEL_OFFSET; } /* Extract reference clock from platform clock source */ static uint32_t get_ref_clk(uint32_t pllglob) { uint32_t arefclkdiv, ref_clk; uint32_t scr_reg; /* * Based on the clock source, read the values from System Manager boot * scratch registers. These values are filled by boot loader based on * hand-off data. */ switch (CLKMGR_PSRC(pllglob)) { case CLKMGR_PLLGLOB_PSRC_EOSC1: scr_reg = SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1); ref_clk = sys_read32(scr_reg); break; case CLKMGR_PLLGLOB_PSRC_INTOSC: ref_clk = CLKMGR_INTOSC_HZ; break; case CLKMGR_PLLGLOB_PSRC_F2S: scr_reg = SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2); ref_clk = sys_read32(scr_reg); break; default: ref_clk = 0; LOG_ERR("Invalid VCO input clock source"); break; } /* Reference clock divider, to get the effective reference clock. */ arefclkdiv = CLKMGR_PLLGLOB_AREFCLKDIV(pllglob); ref_clk /= arefclkdiv; return ref_clk; } /* Calculate clock frequency based on parameter */ static uint32_t get_clk_freq(uint32_t psrc_reg, uint32_t main_pllc, uint32_t per_pllc) { uint32_t clk_psrc, mdiv, ref_clk; uint32_t pllm_reg, pllc_reg, pllc_div, pllglob_reg; clk_psrc = sys_read32(clock_agilex5_ll.mainpll_addr + psrc_reg); switch (CLKMGR_PSRC(clk_psrc)) { case CLKMGR_PSRC_MAIN: pllm_reg = clock_agilex5_ll.mainpll_addr + CLKMGR_MAINPLL_PLLM; pllc_reg = clock_agilex5_ll.mainpll_addr + main_pllc; pllglob_reg = clock_agilex5_ll.mainpll_addr + CLKMGR_MAINPLL_PLLGLOB; break; case CLKMGR_PSRC_PER: pllm_reg = clock_agilex5_ll.peripll_addr + CLKMGR_PERPLL_PLLM; pllc_reg = clock_agilex5_ll.peripll_addr + per_pllc; pllglob_reg = clock_agilex5_ll.peripll_addr + CLKMGR_PERPLL_PLLGLOB; break; default: return 0; } ref_clk = get_ref_clk(sys_read32(pllglob_reg)); mdiv = CLKMGR_PLLM_MDIV(sys_read32(pllm_reg)); ref_clk *= mdiv; /* Clock slice divider ration in binary code. */ pllc_div = CLKMGR_PLLC_DIV(sys_read32(pllc_reg)); return ref_clk / pllc_div; } /* Return L3 interconnect clock */ uint32_t get_l3_clk(void) { uint32_t l3_clk; l3_clk = get_clk_freq(CLKMGR_MAINPLL_NOCCLK, CLKMGR_MAINPLL_PLLC1, CLKMGR_PERPLL_PLLC1); return l3_clk; } /* Calculate clock frequency to be used for mpu */ uint32_t get_mpu_clk(void) { uint32_t mpu_clk; mpu_clk = get_clk_freq(CLKMGR_MAINPLL_MPUCLK, CLKMGR_MAINPLL_PLLC0, CLKMGR_PERPLL_PLLC0); return mpu_clk; } /* Calculate clock frequency to be used for watchdog timer */ uint32_t get_wdt_clk(void) { uint32_t l4_sys_clk; l4_sys_clk = (get_l3_clk() >> 2); return l4_sys_clk; } /* Calculate clock frequency to be used for UART driver */ uint32_t get_uart_clk(void) { uint32_t mainpll_nocdiv, l4_sp_clk; mainpll_nocdiv = sys_read32(clock_agilex5_ll.mainpll_addr + CLKMGR_MAINPLL_NOCDIV); mainpll_nocdiv = CLKMGR_MAINPLL_L4SPDIV(mainpll_nocdiv); l4_sp_clk = (get_l3_clk() >> mainpll_nocdiv); return l4_sp_clk; } /* Calculate clock frequency to be used for SDMMC driver */ uint32_t get_mmc_clk(void) { uint32_t sdmmc_ctr, mmc_clk; mmc_clk = get_clk_freq(CLKMGR_INTEL_SDMMCCTR, CLKMGR_MAINPLL_PLLC3, CLKMGR_PERPLL_PLLC3); sdmmc_ctr = sys_read32(clock_agilex5_ll.ctl_addr + CLKMGR_INTEL_SDMMCCTR); sdmmc_ctr = CLKMGR_INTEL_SDMMC_CNT(sdmmc_ctr); mmc_clk = ((mmc_clk / sdmmc_ctr) >> 2); return mmc_clk; } /* Calculate clock frequency to be used for Timer driver */ uint32_t get_timer_clk(void) { uint32_t l4_sys_clk; l4_sys_clk = (get_l3_clk() >> 2); return l4_sys_clk; }