| /* |
| * Copyright (c) 2019-2022, Intel Corporation. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <arch.h> |
| #include <arch_helpers.h> |
| #include <assert.h> |
| #include <drivers/delay_timer.h> |
| #include <lib/mmio.h> |
| #include <platform_def.h> |
| |
| #include "s10_clock_manager.h" |
| #include "socfpga_handoff.h" |
| #include "socfpga_system_manager.h" |
| |
| |
| void wait_pll_lock(void) |
| { |
| uint32_t data; |
| |
| do { |
| data = mmio_read_32(ALT_CLKMGR + ALT_CLKMGR_STAT); |
| } while ((ALT_CLKMGR_STAT_MAINPLLLOCKED(data) == 0) || |
| (ALT_CLKMGR_STAT_PERPLLLOCKED(data) == 0)); |
| } |
| |
| void wait_fsm(void) |
| { |
| uint32_t data; |
| |
| do { |
| data = mmio_read_32(ALT_CLKMGR + ALT_CLKMGR_STAT); |
| } while (ALT_CLKMGR_STAT_BUSY(data) == ALT_CLKMGR_STAT_BUSY_E_BUSY); |
| } |
| |
| void config_clkmgr_handoff(handoff *hoff_ptr) |
| { |
| uint32_t m_div, refclk_div, mscnt, hscnt; |
| |
| /* Bypass all mainpllgrp's clocks */ |
| mmio_write_32(ALT_CLKMGR_MAINPLL + |
| ALT_CLKMGR_MAINPLL_BYPASS, |
| 0x7); |
| wait_fsm(); |
| /* Bypass all perpllgrp's clocks */ |
| mmio_write_32(ALT_CLKMGR_PERPLL + |
| ALT_CLKMGR_PERPLL_BYPASS, |
| 0x7f); |
| wait_fsm(); |
| |
| /* Setup main PLL dividers */ |
| m_div = ALT_CLKMGR_MAINPLL_FDBCK_MDIV(hoff_ptr->main_pll_fdbck); |
| refclk_div = ALT_CLKMGR_MAINPLL_PLLGLOB_REFCLKDIV( |
| hoff_ptr->main_pll_pllglob); |
| mscnt = 200 / ((6 + m_div) / refclk_div); |
| hscnt = (m_div + 6) * mscnt / refclk_div - 9; |
| |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_PLLGLOB, |
| hoff_ptr->main_pll_pllglob); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_FDBCK, |
| hoff_ptr->main_pll_fdbck); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_VCOCALIB, |
| ALT_CLKMGR_MAINPLL_VCOCALIB_HSCNT_SET(hscnt) | |
| ALT_CLKMGR_MAINPLL_VCOCALIB_MSCNT_SET(mscnt)); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_PLLC0, |
| hoff_ptr->main_pll_pllc0); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_PLLC1, |
| hoff_ptr->main_pll_pllc1); |
| |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_NOCDIV, |
| hoff_ptr->main_pll_nocdiv); |
| |
| /* Setup peripheral PLL dividers */ |
| m_div = ALT_CLKMGR_PERPLL_FDBCK_MDIV(hoff_ptr->per_pll_fdbck); |
| refclk_div = ALT_CLKMGR_PERPLL_PLLGLOB_REFCLKDIV( |
| hoff_ptr->per_pll_pllglob); |
| mscnt = 200 / ((6 + m_div) / refclk_div); |
| hscnt = (m_div + 6) * mscnt / refclk_div - 9; |
| |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_PLLGLOB, |
| hoff_ptr->per_pll_pllglob); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_FDBCK, |
| hoff_ptr->per_pll_fdbck); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_VCOCALIB, |
| ALT_CLKMGR_PERPLL_VCOCALIB_HSCNT_SET(hscnt) | |
| ALT_CLKMGR_PERPLL_VCOCALIB_MSCNT_SET(mscnt)); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_PLLC0, |
| hoff_ptr->per_pll_pllc0); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_PLLC1, |
| hoff_ptr->per_pll_pllc1); |
| |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_GPIODIV, |
| ALT_CLKMGR_PERPLL_GPIODIV_GPIODBCLK_SET( |
| hoff_ptr->per_pll_gpiodiv)); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_EMACCTL, |
| hoff_ptr->per_pll_emacctl); |
| |
| |
| /* Take both PLL out of reset and power up */ |
| mmio_setbits_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_PLLGLOB, |
| ALT_CLKMGR_MAINPLL_PLLGLOB_PD_SET_MSK | |
| ALT_CLKMGR_MAINPLL_PLLGLOB_RST_SET_MSK); |
| mmio_setbits_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_PLLGLOB, |
| ALT_CLKMGR_PERPLL_PLLGLOB_PD_SET_MSK | |
| ALT_CLKMGR_PERPLL_PLLGLOB_RST_SET_MSK); |
| |
| wait_pll_lock(); |
| |
| /* Dividers for C2 to C9 only init after PLLs are lock. */ |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_MPUCLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_NOCCLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR2CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR3CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR4CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR5CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR6CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR7CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR8CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR9CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR2CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR3CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR4CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR5CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR6CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR7CLK, 0xff); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR8CLK, 0xff); |
| |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_MPUCLK, |
| hoff_ptr->main_pll_mpuclk); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_NOCCLK, |
| hoff_ptr->main_pll_nocclk); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR2CLK, |
| hoff_ptr->main_pll_cntr2clk); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR3CLK, |
| hoff_ptr->main_pll_cntr3clk); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR4CLK, |
| hoff_ptr->main_pll_cntr4clk); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR5CLK, |
| hoff_ptr->main_pll_cntr5clk); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR6CLK, |
| hoff_ptr->main_pll_cntr6clk); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR7CLK, |
| hoff_ptr->main_pll_cntr7clk); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR8CLK, |
| hoff_ptr->main_pll_cntr8clk); |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR9CLK, |
| hoff_ptr->main_pll_cntr9clk); |
| |
| /* Peripheral PLL Clock Source and Counters/Divider */ |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR2CLK, |
| hoff_ptr->per_pll_cntr2clk); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR3CLK, |
| hoff_ptr->per_pll_cntr3clk); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR4CLK, |
| hoff_ptr->per_pll_cntr4clk); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR5CLK, |
| hoff_ptr->per_pll_cntr5clk); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR6CLK, |
| hoff_ptr->per_pll_cntr6clk); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR7CLK, |
| hoff_ptr->per_pll_cntr7clk); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR8CLK, |
| hoff_ptr->per_pll_cntr8clk); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_CNTR9CLK, |
| hoff_ptr->per_pll_cntr9clk); |
| |
| /* Take all PLLs out of bypass */ |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_BYPASS, 0); |
| wait_fsm(); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_BYPASS, 0); |
| wait_fsm(); |
| |
| /* Set safe mode/ out of boot mode */ |
| mmio_clrbits_32(ALT_CLKMGR + ALT_CLKMGR_CTRL, |
| ALT_CLKMGR_CTRL_BOOTMODE_SET_MSK); |
| wait_fsm(); |
| |
| /* 10 Enable mainpllgrp's software-managed clock */ |
| mmio_write_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_EN, |
| ALT_CLKMGR_MAINPLL_EN_RESET); |
| mmio_write_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_EN, |
| ALT_CLKMGR_PERPLL_EN_RESET); |
| |
| /* Clear loss lock interrupt status register that */ |
| /* might be set during configuration */ |
| mmio_write_32(ALT_CLKMGR + ALT_CLKMGR_INTRCLR, |
| ALT_CLKMGR_INTRCLR_MAINLOCKLOST_SET_MSK | |
| ALT_CLKMGR_INTRCLR_PERLOCKLOST_SET_MSK); |
| |
| /* Pass clock source frequency into scratch register */ |
| mmio_write_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1), |
| hoff_ptr->hps_osc_clk_h); |
| mmio_write_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2), |
| hoff_ptr->fpga_clk_hz); |
| |
| } |
| |
| /* Extract reference clock from platform clock source */ |
| uint32_t get_ref_clk(uint32_t pllglob) |
| { |
| uint32_t data32, mdiv, refclkdiv, ref_clk; |
| uint32_t scr_reg; |
| |
| switch (ALT_CLKMGR_PSRC(pllglob)) { |
| case ALT_CLKMGR_PLLGLOB_PSRC_EOSC1: |
| scr_reg = SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_1); |
| ref_clk = mmio_read_32(scr_reg); |
| break; |
| case ALT_CLKMGR_PLLGLOB_PSRC_INTOSC: |
| ref_clk = ALT_CLKMGR_INTOSC_HZ; |
| break; |
| case ALT_CLKMGR_PLLGLOB_PSRC_F2S: |
| scr_reg = SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_2); |
| ref_clk = mmio_read_32(scr_reg); |
| break; |
| default: |
| ref_clk = 0; |
| assert(0); |
| break; |
| } |
| |
| refclkdiv = ALT_CLKMGR_MAINPLL_PLLGLOB_REFCLKDIV(pllglob); |
| data32 = mmio_read_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_FDBCK); |
| mdiv = ALT_CLKMGR_MAINPLL_FDBCK_MDIV(data32); |
| |
| ref_clk = (ref_clk / refclkdiv) * (6 + mdiv); |
| |
| return ref_clk; |
| } |
| |
| /* Calculate L3 interconnect main clock */ |
| uint32_t get_l3_clk(uint32_t ref_clk) |
| { |
| uint32_t noc_base_clk, l3_clk, noc_clk, data32; |
| uint32_t pllc1_reg; |
| |
| noc_clk = mmio_read_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_NOCCLK); |
| |
| switch (ALT_CLKMGR_PSRC(noc_clk)) { |
| case ALT_CLKMGR_SRC_MAIN: |
| pllc1_reg = ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_PLLC1; |
| break; |
| case ALT_CLKMGR_SRC_PER: |
| pllc1_reg = ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_PLLC1; |
| break; |
| default: |
| pllc1_reg = 0; |
| assert(0); |
| break; |
| } |
| |
| data32 = mmio_read_32(pllc1_reg); |
| noc_base_clk = ref_clk / (data32 & 0xff); |
| l3_clk = noc_base_clk / (noc_clk + 1); |
| |
| return l3_clk; |
| } |
| |
| /* Calculate clock frequency to be used for watchdog timer */ |
| uint32_t get_wdt_clk(void) |
| { |
| uint32_t data32, ref_clk, l3_clk, l4_sys_clk; |
| |
| data32 = mmio_read_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_PLLGLOB); |
| ref_clk = get_ref_clk(data32); |
| |
| l3_clk = get_l3_clk(ref_clk); |
| |
| l4_sys_clk = l3_clk / 4; |
| |
| return l4_sys_clk; |
| } |
| |
| /* Calculate clock frequency to be used for UART driver */ |
| uint32_t get_uart_clk(void) |
| { |
| uint32_t data32, ref_clk, l3_clk, l4_sp_clk; |
| |
| data32 = mmio_read_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_PLLGLOB); |
| ref_clk = get_ref_clk(data32); |
| |
| l3_clk = get_l3_clk(ref_clk); |
| |
| data32 = mmio_read_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_NOCDIV); |
| data32 = (data32 >> 16) & 0x3; |
| data32 = 1 << data32; |
| |
| l4_sp_clk = (l3_clk / data32); |
| |
| return l4_sp_clk; |
| } |
| |
| /* Calculate clock frequency to be used for SDMMC driver */ |
| uint32_t get_mmc_clk(void) |
| { |
| uint32_t data32, ref_clk, l3_clk, mmc_clk; |
| |
| data32 = mmio_read_32(ALT_CLKMGR_PERPLL + ALT_CLKMGR_PERPLL_PLLGLOB); |
| ref_clk = get_ref_clk(data32); |
| |
| l3_clk = get_l3_clk(ref_clk); |
| |
| data32 = mmio_read_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_CNTR6CLK); |
| mmc_clk = (l3_clk / (data32 + 1)) / 4; |
| |
| return mmc_clk; |
| } |
| |
| /* Get cpu freq clock */ |
| uint32_t get_cpu_clk(void) |
| { |
| uint32_t data32, ref_clk, cpu_clk; |
| |
| data32 = mmio_read_32(ALT_CLKMGR_MAINPLL + ALT_CLKMGR_MAINPLL_PLLGLOB); |
| ref_clk = get_ref_clk(data32); |
| |
| cpu_clk = get_l3_clk(ref_clk)/PLAT_SYS_COUNTER_CONVERT_TO_MHZ; |
| |
| return cpu_clk; |
| } |