| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright 2014 Broadcom Corporation. |
| */ |
| |
| #include <asm/io.h> |
| #include <asm/iproc-common/armpll.h> |
| #include <asm/iproc-common/sysmap.h> |
| |
| #define NELEMS(x) (sizeof(x) / sizeof(x[0])) |
| |
| struct armpll_parameters { |
| unsigned int mode; |
| unsigned int ndiv_int; |
| unsigned int ndiv_frac; |
| unsigned int pdiv; |
| unsigned int freqid; |
| }; |
| |
| struct armpll_parameters armpll_clk_tab[] = { |
| { 25, 64, 1, 1, 0}, |
| { 100, 64, 1, 1, 2}, |
| { 400, 64, 1, 1, 6}, |
| { 448, 71, 713050, 1, 6}, |
| { 500, 80, 1, 1, 6}, |
| { 560, 89, 629145, 1, 6}, |
| { 600, 96, 1, 1, 6}, |
| { 800, 64, 1, 1, 7}, |
| { 896, 71, 713050, 1, 7}, |
| { 1000, 80, 1, 1, 7}, |
| { 1100, 88, 1, 1, 7}, |
| { 1120, 89, 629145, 1, 7}, |
| { 1200, 96, 1, 1, 7}, |
| { 1300, 104, 1, 1, 7}, |
| { 1350, 108, 1, 1, 7}, |
| { 1400, 112, 1, 1, 7}, |
| }; |
| |
| uint32_t armpll_config(uint32_t clkmhz) |
| { |
| uint32_t freqid; |
| uint32_t ndiv_frac; |
| uint32_t pll; |
| uint32_t status = 1; |
| uint32_t timeout_countdown; |
| int i; |
| |
| for (i = 0; i < NELEMS(armpll_clk_tab); i++) { |
| if (armpll_clk_tab[i].mode == clkmhz) { |
| status = 0; |
| break; |
| } |
| } |
| |
| if (status) { |
| printf("Error: Clock configuration not supported\n"); |
| goto armpll_config_done; |
| } |
| |
| /* Enable write access */ |
| writel(IPROC_REG_WRITE_ACCESS, IHOST_PROC_CLK_WR_ACCESS); |
| |
| if (clkmhz == 25) |
| freqid = 0; |
| else |
| freqid = 2; |
| |
| /* Bypass ARM clock and run on sysclk */ |
| writel(1 << IHOST_PROC_CLK_POLICY_FREQ__PRIV_ACCESS_MODE | |
| freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY3_FREQ_R | |
| freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY2_FREQ_R | |
| freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY1_FREQ_R | |
| freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY0_FREQ_R, |
| IHOST_PROC_CLK_POLICY_FREQ); |
| |
| writel(1 << IHOST_PROC_CLK_POLICY_CTL__GO | |
| 1 << IHOST_PROC_CLK_POLICY_CTL__GO_AC, |
| IHOST_PROC_CLK_POLICY_CTL); |
| |
| /* Poll CCU until operation complete */ |
| timeout_countdown = 0x100000; |
| while (readl(IHOST_PROC_CLK_POLICY_CTL) & |
| (1 << IHOST_PROC_CLK_POLICY_CTL__GO)) { |
| timeout_countdown--; |
| if (timeout_countdown == 0) { |
| printf("CCU polling timedout\n"); |
| status = 1; |
| goto armpll_config_done; |
| } |
| } |
| |
| if (clkmhz == 25 || clkmhz == 100) { |
| status = 0; |
| goto armpll_config_done; |
| } |
| |
| /* Now it is safe to program the PLL */ |
| pll = readl(IHOST_PROC_CLK_PLLARMB); |
| pll &= ~((1 << IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_WIDTH) - 1); |
| ndiv_frac = |
| ((1 << IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_WIDTH) - 1) & |
| (armpll_clk_tab[i].ndiv_frac << |
| IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_R); |
| pll |= ndiv_frac; |
| writel(pll, IHOST_PROC_CLK_PLLARMB); |
| |
| writel(1 << IHOST_PROC_CLK_PLLARMA__PLLARM_LOCK | |
| armpll_clk_tab[i].ndiv_int << |
| IHOST_PROC_CLK_PLLARMA__PLLARM_NDIV_INT_R | |
| armpll_clk_tab[i].pdiv << |
| IHOST_PROC_CLK_PLLARMA__PLLARM_PDIV_R | |
| 1 << IHOST_PROC_CLK_PLLARMA__PLLARM_SOFT_RESETB, |
| IHOST_PROC_CLK_PLLARMA); |
| |
| /* Poll ARM PLL Lock until operation complete */ |
| timeout_countdown = 0x100000; |
| while (readl(IHOST_PROC_CLK_PLLARMA) & |
| (1 << IHOST_PROC_CLK_PLLARMA__PLLARM_LOCK)) { |
| timeout_countdown--; |
| if (timeout_countdown == 0) { |
| printf("ARM PLL lock failed\n"); |
| status = 1; |
| goto armpll_config_done; |
| } |
| } |
| |
| pll = readl(IHOST_PROC_CLK_PLLARMA); |
| pll |= (1 << IHOST_PROC_CLK_PLLARMA__PLLARM_SOFT_POST_RESETB); |
| writel(pll, IHOST_PROC_CLK_PLLARMA); |
| |
| /* Set the policy */ |
| writel(1 << IHOST_PROC_CLK_POLICY_FREQ__PRIV_ACCESS_MODE | |
| armpll_clk_tab[i].freqid << |
| IHOST_PROC_CLK_POLICY_FREQ__POLICY3_FREQ_R | |
| armpll_clk_tab[i].freqid << |
| IHOST_PROC_CLK_POLICY_FREQ__POLICY2_FREQ_R | |
| armpll_clk_tab[i].freqid << |
| IHOST_PROC_CLK_POLICY_FREQ__POLICY1_FREQ_R | |
| armpll_clk_tab[i+4].freqid << |
| IHOST_PROC_CLK_POLICY_FREQ__POLICY0_FREQ_R, |
| IHOST_PROC_CLK_POLICY_FREQ); |
| |
| writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_CORE0_CLKGATE); |
| writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_CORE1_CLKGATE); |
| writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_ARM_SWITCH_CLKGATE); |
| writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_ARM_PERIPH_CLKGATE); |
| writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_APB0_CLKGATE); |
| |
| writel(1 << IHOST_PROC_CLK_POLICY_CTL__GO | |
| 1 << IHOST_PROC_CLK_POLICY_CTL__GO_AC, |
| IHOST_PROC_CLK_POLICY_CTL); |
| |
| /* Poll CCU until operation complete */ |
| timeout_countdown = 0x100000; |
| while (readl(IHOST_PROC_CLK_POLICY_CTL) & |
| (1 << IHOST_PROC_CLK_POLICY_CTL__GO)) { |
| timeout_countdown--; |
| if (timeout_countdown == 0) { |
| printf("CCU polling failed\n"); |
| status = 1; |
| goto armpll_config_done; |
| } |
| } |
| |
| status = 0; |
| armpll_config_done: |
| /* Disable access to PLL registers */ |
| writel(0, IHOST_PROC_CLK_WR_ACCESS); |
| |
| return status; |
| } |