Browse Source
The clock controller/manager registers are updated with the correct divider values by bootloader via hand-off data, so now we can use the clock controller to get the clock value of each peripheral during the run time. Signed-off-by: Girisha Dengi <girisha.dengi@intel.com>pull/82829/head
5 changed files with 379 additions and 197 deletions
@ -1,181 +1,254 @@
@@ -1,181 +1,254 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2023, Intel Corporation. |
||||
* Copyright (c) 2022-2024, Intel Corporation. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include <zephyr/kernel.h> |
||||
#include <zephyr/logging/log.h> |
||||
#include <zephyr/arch/cpu.h> |
||||
#include <socfpga_system_manager.h> |
||||
#include <zephyr/sys/__assert.h> |
||||
|
||||
#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) |
||||
static uint32_t get_ref_clk(mm_reg_t pllglob_reg, mm_reg_t pllm_reg) |
||||
{ |
||||
uint32_t arefclkdiv, ref_clk; |
||||
uint32_t scr_reg; |
||||
uint32_t arefclkdiv = 0U; |
||||
uint32_t ref_clk = 0U; |
||||
uint32_t mdiv = 0U; |
||||
uint32_t pllglob_val = 0U; |
||||
uint32_t pllm_val = 0U; |
||||
|
||||
/* Read pllglob and pllm registers */ |
||||
pllglob_val = sys_read32(pllglob_reg); |
||||
pllm_val = sys_read32(pllm_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); |
||||
switch (CLKCTRL_PSRC(pllglob_val)) { |
||||
case CLKCTRL_PLLGLOB_PSRC_EOSC1: |
||||
ref_clk = sys_read32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1)); |
||||
break; |
||||
|
||||
case CLKMGR_PLLGLOB_PSRC_INTOSC: |
||||
ref_clk = CLKMGR_INTOSC_HZ; |
||||
case CLKCTRL_PLLGLOB_PSRC_INTOSC: |
||||
ref_clk = CLKCTRL_INTOSC_HZ; |
||||
break; |
||||
|
||||
case CLKMGR_PLLGLOB_PSRC_F2S: |
||||
scr_reg = SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2); |
||||
ref_clk = sys_read32(scr_reg); |
||||
case CLKCTRL_PLLGLOB_PSRC_F2S: |
||||
ref_clk = sys_read32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2)); |
||||
break; |
||||
|
||||
default: |
||||
ref_clk = 0; |
||||
LOG_ERR("Invalid VCO input clock source"); |
||||
__ASSERT(0, "Invalid input clock source"); |
||||
break; |
||||
} |
||||
|
||||
/* Reference clock divider, to get the effective reference clock. */ |
||||
arefclkdiv = CLKMGR_PLLGLOB_AREFCLKDIV(pllglob); |
||||
/* Get reference clock divider */ |
||||
arefclkdiv = CLKCTRL_PLLGLOB_AREFCLKDIV(pllglob_val); |
||||
__ASSERT(arefclkdiv != 0, "Reference clock divider is zero"); |
||||
ref_clk /= arefclkdiv; |
||||
|
||||
/* Feedback clock divider */ |
||||
mdiv = CLKCTRL_PLLM_MDIV(pllm_val); |
||||
ref_clk *= mdiv; |
||||
|
||||
LOG_DBG("%s: ref_clk %u\n", __func__, ref_clk); |
||||
|
||||
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) |
||||
static uint32_t get_clk_freq(mm_reg_t psrc_reg, mm_reg_t mainpllc_reg, |
||||
mm_reg_t perpllc_reg) |
||||
{ |
||||
uint32_t clk_psrc, mdiv, ref_clk; |
||||
uint32_t pllm_reg, pllc_reg, pllc_div, pllglob_reg; |
||||
uint32_t clock_val = 0U; |
||||
uint32_t clk_psrc = 0U; |
||||
uint32_t pllcx_div = 0U; |
||||
|
||||
/*
|
||||
* Select source for the active 5:1 clock selection when the PLL |
||||
* is not bypassed |
||||
*/ |
||||
clk_psrc = sys_read32(psrc_reg); |
||||
switch (GET_CLKCTRL_CLKSRC(clk_psrc)) { |
||||
case CLKCTRL_CLKSRC_MAIN: |
||||
clock_val = get_ref_clk(CLKCTRL_MAINPLL(PLLGLOB), CLKCTRL_MAINPLL(PLLM)); |
||||
pllcx_div = (sys_read32(mainpllc_reg) & CLKCTRL_PLLCX_DIV_MSK); |
||||
__ASSERT(pllcx_div != 0, "Main PLLC clock divider is zero"); |
||||
clock_val /= pllcx_div; |
||||
break; |
||||
|
||||
case CLKCTRL_CLKSRC_PER: |
||||
clock_val = get_ref_clk(CLKCTRL_PERPLL(PLLGLOB), CLKCTRL_PERPLL(PLLM)); |
||||
pllcx_div = (sys_read32(perpllc_reg) & CLKCTRL_PLLCX_DIV_MSK); |
||||
__ASSERT(pllcx_div != 0, "Peripheral PLLC clock divider is zero"); |
||||
clock_val /= pllcx_div; |
||||
break; |
||||
|
||||
clk_psrc = sys_read32(clock_agilex5_ll.mainpll_addr + psrc_reg); |
||||
case CLKCTRL_CLKSRC_OSC1: |
||||
clock_val = sys_read32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1)); |
||||
break; |
||||
|
||||
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; |
||||
case CLKCTRL_CLKSRC_INTOSC: |
||||
clock_val = CLKCTRL_INTOSC_HZ; |
||||
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; |
||||
case CLKCTRL_CLKSRC_FPGA: |
||||
clock_val = sys_read32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2)); |
||||
break; |
||||
|
||||
default: |
||||
return 0; |
||||
__ASSERT(0, "Invalid clock source select"); |
||||
break; |
||||
} |
||||
|
||||
ref_clk = get_ref_clk(sys_read32(pllglob_reg)); |
||||
mdiv = CLKMGR_PLLM_MDIV(sys_read32(pllm_reg)); |
||||
ref_clk *= mdiv; |
||||
LOG_DBG("%s: clock source %lu and its value %u\n", |
||||
__func__, GET_CLKCTRL_CLKSRC(clk_psrc), clock_val); |
||||
|
||||
/* Clock slice divider ration in binary code. */ |
||||
pllc_div = CLKMGR_PLLC_DIV(sys_read32(pllc_reg)); |
||||
return clock_val; |
||||
} |
||||
|
||||
return ref_clk / pllc_div; |
||||
/* Get L3 free clock */ |
||||
static uint32_t get_l3_main_free_clk(void) |
||||
{ |
||||
return get_clk_freq(CLKCTRL_MAINPLL(NOCCLK), |
||||
CLKCTRL_MAINPLL(PLLC3), |
||||
CLKCTRL_PERPLL(PLLC1)); |
||||
} |
||||
|
||||
/* Return L3 interconnect clock */ |
||||
uint32_t get_l3_clk(void) |
||||
/* Get L4 mp clock */ |
||||
static uint32_t get_l4_mp_clk(void) |
||||
{ |
||||
uint32_t l3_main_free_clk = get_l3_main_free_clk(); |
||||
uint32_t mainpll_nocdiv_l4mp = BIT(GET_CLKCTRL_MAINPLL_NOCDIV_L4MP( |
||||
sys_read32(CLKCTRL_MAINPLL(NOCDIV)))); |
||||
|
||||
uint32_t l4_mp_clk = (l3_main_free_clk / mainpll_nocdiv_l4mp); |
||||
|
||||
return l4_mp_clk; |
||||
} |
||||
|
||||
/*
|
||||
* Get L4 sp clock. |
||||
* "l4_sp_clk" (100MHz) will be used for slow peripherals like UART, I2C, |
||||
* Timers ...etc. |
||||
*/ |
||||
static uint32_t get_l4_sp_clk(void) |
||||
{ |
||||
uint32_t l3_clk; |
||||
uint32_t l3_main_free_clk = get_l3_main_free_clk(); |
||||
uint32_t mainpll_nocdiv_l4sp = BIT(GET_CLKCTRL_MAINPLL_NOCDIV_L4SP( |
||||
sys_read32(CLKCTRL_MAINPLL(NOCDIV)))); |
||||
|
||||
l3_clk = get_clk_freq(CLKMGR_MAINPLL_NOCCLK, CLKMGR_MAINPLL_PLLC1, CLKMGR_PERPLL_PLLC1); |
||||
uint32_t l4_sp_clk = (l3_main_free_clk / mainpll_nocdiv_l4sp); |
||||
|
||||
return l3_clk; |
||||
return l4_sp_clk; |
||||
} |
||||
|
||||
/* Calculate clock frequency to be used for mpu */ |
||||
/* Get MPU clock */ |
||||
uint32_t get_mpu_clk(void) |
||||
{ |
||||
uint32_t mpu_clk; |
||||
uint8_t cpu_id = arch_curr_cpu()->id; |
||||
uint32_t ctr_reg = 0U; |
||||
uint32_t clock_val = 0U; |
||||
|
||||
if (cpu_id > CLKCTRL_CPU_ID_CORE1) { |
||||
clock_val = get_clk_freq(CLKCTRL_CTLGRP(CORE23CTR), |
||||
CLKCTRL_MAINPLL(PLLC0), |
||||
CLKCTRL_PERPLL(PLLC0)); |
||||
} else { |
||||
clock_val = get_clk_freq(CLKCTRL_CTLGRP(CORE01CTR), |
||||
CLKCTRL_MAINPLL(PLLC1), |
||||
CLKCTRL_PERPLL(PLLC0)); |
||||
} |
||||
|
||||
switch (cpu_id) { |
||||
case CLKCTRL_CPU_ID_CORE0: |
||||
case CLKCTRL_CPU_ID_CORE1: |
||||
ctr_reg = CLKCTRL_CTLGRP(CORE01CTR); |
||||
break; |
||||
|
||||
mpu_clk = get_clk_freq(CLKMGR_MAINPLL_MPUCLK, CLKMGR_MAINPLL_PLLC0, CLKMGR_PERPLL_PLLC0); |
||||
case CLKCTRL_CPU_ID_CORE2: |
||||
ctr_reg = CLKCTRL_CTLGRP(CORE2CTR); |
||||
break; |
||||
|
||||
case CLKCTRL_CPU_ID_CORE3: |
||||
ctr_reg = CLKCTRL_CTLGRP(CORE3CTR); |
||||
break; |
||||
|
||||
return mpu_clk; |
||||
default: |
||||
break; |
||||
} |
||||
|
||||
/* Division setting for ping pong counter in clock slice */ |
||||
clock_val /= 1 + (sys_read32(ctr_reg) & CLKCTRL_PLLCX_DIV_MSK); |
||||
|
||||
return clock_val; |
||||
} |
||||
|
||||
/* Calculate clock frequency to be used for watchdog timer */ |
||||
uint32_t get_wdt_clk(void) |
||||
{ |
||||
uint32_t l4_sys_clk; |
||||
uint32_t l3_main_free_clk = get_l3_main_free_clk(); |
||||
uint32_t mainpll_nocdiv_l4sysfreeclk = BIT(GET_CLKCTRL_MAINPLL_NOCDIV_L4SYSFREE( |
||||
sys_read32(CLKCTRL_MAINPLL(NOCDIV)))); |
||||
uint32_t l4_sys_free_clk = (l3_main_free_clk / mainpll_nocdiv_l4sysfreeclk); |
||||
|
||||
l4_sys_clk = (get_l3_clk() >> 2); |
||||
|
||||
return l4_sys_clk; |
||||
return l4_sys_free_clk; |
||||
} |
||||
|
||||
/* Calculate clock frequency to be used for UART driver */ |
||||
/* Get 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; |
||||
return get_l4_sp_clk(); |
||||
} |
||||
|
||||
/* Calculate clock frequency to be used for SDMMC driver */ |
||||
uint32_t get_mmc_clk(void) |
||||
uint32_t get_sdmmc_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); |
||||
uint32_t l4_mp_clk = get_l4_mp_clk(); |
||||
uint32_t mainpll_nocdiv = sys_read32(CLKCTRL_MAINPLL(NOCDIV)); |
||||
uint32_t sdmmc_clk = l4_mp_clk / BIT(GET_CLKCTRL_MAINPLL_NOCDIV_SPHY(mainpll_nocdiv)); |
||||
|
||||
return mmc_clk; |
||||
return sdmmc_clk; |
||||
} |
||||
|
||||
/* Calculate clock frequency to be used for Timer driver */ |
||||
uint32_t get_timer_clk(void) |
||||
{ |
||||
uint32_t l4_sys_clk; |
||||
return get_l4_sp_clk(); |
||||
} |
||||
|
||||
/* Calculate clock frequency to be used for QSPI driver */ |
||||
uint32_t get_qspi_clk(void) |
||||
{ |
||||
uint32_t scr_reg, ref_clk; |
||||
|
||||
l4_sys_clk = (get_l3_clk() >> 2); |
||||
scr_reg = SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_0); |
||||
ref_clk = sys_read32(scr_reg); |
||||
|
||||
return l4_sys_clk; |
||||
/*
|
||||
* In ATF, the qspi clock is divided by 1000 and loaded in scratch cold register 0 |
||||
* So in Zephyr, reverting back the clock frequency by multiplying by 1000. |
||||
*/ |
||||
return (ref_clk * 1000); |
||||
} |
||||
|
||||
/* Calculate clock frequency to be used for I2C driver */ |
||||
uint32_t get_i2c_clk(void) |
||||
{ |
||||
return get_l4_sp_clk(); |
||||
} |
||||
|
||||
/* Calculate clock frequency to be used for I3C driver */ |
||||
uint32_t get_i3c_clk(void) |
||||
{ |
||||
return get_l4_mp_clk(); |
||||
} |
||||
|
Loading…
Reference in new issue