/* * Copyright 2014 Broadcom Corporation. * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #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}, }; 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; }