Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
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

/*
* 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();
}