zephyr/drivers/clock_control/clock_control_agilex5_ll.c
Girisha Dengi 2ca6ffcd79 drivers: clock_control: clock driver for Intel Agilex5 platform
This is Intel's proprietary IP which supply the clock for all the
system peripherals. Clock manager is initialized only one time
during boot up by FSBL (ATF BL2) based on external user settings.

Signed-off-by: Girisha Dengi <girisha.dengi@intel.com>
2023-07-25 16:58:01 +00:00

182 lines
4.6 KiB
C

/*
* Copyright (c) 2022-2023, Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <socfpga_system_manager.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)
{
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;
}