You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
6.2 KiB
254 lines
6.2 KiB
/* |
|
* 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); |
|
|
|
/* Extract reference clock from platform clock source */ |
|
static uint32_t get_ref_clk(mm_reg_t pllglob_reg, mm_reg_t pllm_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 (CLKCTRL_PSRC(pllglob_val)) { |
|
case CLKCTRL_PLLGLOB_PSRC_EOSC1: |
|
ref_clk = sys_read32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1)); |
|
break; |
|
|
|
case CLKCTRL_PLLGLOB_PSRC_INTOSC: |
|
ref_clk = CLKCTRL_INTOSC_HZ; |
|
break; |
|
|
|
case CLKCTRL_PLLGLOB_PSRC_F2S: |
|
ref_clk = sys_read32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2)); |
|
break; |
|
|
|
default: |
|
ref_clk = 0; |
|
__ASSERT(0, "Invalid input clock source"); |
|
break; |
|
} |
|
|
|
/* 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(mm_reg_t psrc_reg, mm_reg_t mainpllc_reg, |
|
mm_reg_t perpllc_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; |
|
|
|
case CLKCTRL_CLKSRC_OSC1: |
|
clock_val = sys_read32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1)); |
|
break; |
|
|
|
case CLKCTRL_CLKSRC_INTOSC: |
|
clock_val = CLKCTRL_INTOSC_HZ; |
|
break; |
|
|
|
case CLKCTRL_CLKSRC_FPGA: |
|
clock_val = sys_read32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2)); |
|
break; |
|
|
|
default: |
|
__ASSERT(0, "Invalid clock source select"); |
|
break; |
|
} |
|
|
|
LOG_DBG("%s: clock source %lu and its value %u\n", |
|
__func__, GET_CLKCTRL_CLKSRC(clk_psrc), clock_val); |
|
|
|
return clock_val; |
|
} |
|
|
|
/* 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)); |
|
} |
|
|
|
/* 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_main_free_clk = get_l3_main_free_clk(); |
|
uint32_t mainpll_nocdiv_l4sp = BIT(GET_CLKCTRL_MAINPLL_NOCDIV_L4SP( |
|
sys_read32(CLKCTRL_MAINPLL(NOCDIV)))); |
|
|
|
uint32_t l4_sp_clk = (l3_main_free_clk / mainpll_nocdiv_l4sp); |
|
|
|
return l4_sp_clk; |
|
} |
|
|
|
/* Get MPU clock */ |
|
uint32_t get_mpu_clk(void) |
|
{ |
|
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; |
|
|
|
case CLKCTRL_CPU_ID_CORE2: |
|
ctr_reg = CLKCTRL_CTLGRP(CORE2CTR); |
|
break; |
|
|
|
case CLKCTRL_CPU_ID_CORE3: |
|
ctr_reg = CLKCTRL_CTLGRP(CORE3CTR); |
|
break; |
|
|
|
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 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); |
|
|
|
return l4_sys_free_clk; |
|
} |
|
|
|
/* Get clock frequency to be used for UART driver */ |
|
uint32_t get_uart_clk(void) |
|
{ |
|
return get_l4_sp_clk(); |
|
} |
|
|
|
/* Calculate clock frequency to be used for SDMMC driver */ |
|
uint32_t get_sdmmc_clk(void) |
|
{ |
|
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 sdmmc_clk; |
|
} |
|
|
|
/* Calculate clock frequency to be used for Timer driver */ |
|
uint32_t get_timer_clk(void) |
|
{ |
|
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; |
|
|
|
scr_reg = SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_0); |
|
ref_clk = sys_read32(scr_reg); |
|
|
|
/* |
|
* 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(); |
|
}
|
|
|