From 7725240b7c4af75283c7ee1802cd416d4e8b2965 Mon Sep 17 00:00:00 2001 From: Christoph Muellner Date: Wed, 22 Feb 2017 16:46:36 +0100 Subject: sun50iw1p1: Generalize CPU PLL setup code for arbitrary frequencies. Instead of having hard coded values for the CPU PLL control register, we calculate the PLL factors with the following rules: * M is always 1 (granularity is 24 MHz) * K is keept as low as possible Signed-off-by: Christoph Muellner --- plat/sun50iw1p1/sunxi_clocks.c | 70 ++++++++++++++++++++++++++++++++--------- plat/sun50iw1p1/sunxi_private.h | 1 + 2 files changed, 56 insertions(+), 15 deletions(-) (limited to 'plat') diff --git a/plat/sun50iw1p1/sunxi_clocks.c b/plat/sun50iw1p1/sunxi_clocks.c index 1f8a002..ac7a314 100644 --- a/plat/sun50iw1p1/sunxi_clocks.c +++ b/plat/sun50iw1p1/sunxi_clocks.c @@ -33,12 +33,6 @@ #include #include "sunxi_private.h" -#define PLL_CPUX_1008MHZ 0x1410 -#define PLL_CPUX_816MHZ 0x1010 -#define PLL_CPUX_408MHZ 0x1000 - - - static void mmio_clrsetbits32(uintptr_t addr, uint32_t mask, uint32_t bits) { uint32_t regval = mmio_read_32(addr); @@ -65,16 +59,42 @@ static int pll_wait_until_stable(uintptr_t addr) return 0; } -int sunxi_setup_clocks(uint16_t socid) +static void get_pll1_factors(unsigned int *freq_mhz, + uint8_t *n, uint8_t *k, uint8_t *m) { - uint32_t reg; + /* + * We set M=1 (i.e. we only allow parent_rate steps). + * The reason for that is, that we've observed stability issues + * if M gets higher. Of course it makes the calculation faster as well. + */ + const uint8_t N_max = 32; /* 5 bits */ + const unsigned int step_mhz = 24; /* MHz */ + uint8_t N, K; /* multiplicators (e.g. N = n+1) */ + + /* We try to keep K as low as possible. */ + if (*freq_mhz < step_mhz*(N_max*1-1)) + K = 1; + else if (*freq_mhz < step_mhz*(N_max*2-1)) + K = 2; + else if (*freq_mhz < step_mhz*(N_max*3-1)) + K = 3; + else + K = 4; + + N = *freq_mhz / (step_mhz * K); + + *freq_mhz = (step_mhz * N * K); + *n = N-1; + *k = K-1; + *m = 0; +} - NOTICE("BL3-1: Reprogramming clocks for a %d MHz\n"); +void sun50i_set_cpu_pll(unsigned int freq_mhz) +{ + uint8_t n, k, m; + uint32_t reg; - /* Avoid reprogramming PERIPH0 if not necessary */ - reg = mmio_read_32(CCMU_PLL_PERIPH0_CTRL_REG); - if ((reg & 0x0fffffff) != 0x41811) /* is not at 600 MHz? */ - mmio_write_32(CCMU_PLL_PERIPH0_CTRL_REG, 0x80041811); + NOTICE("BL3-1: Reprogramming clocks for %d MHz\n", freq_mhz); /* Set up dividers (suitable for the target clock frequency) and switch CPUX (and thus AXI & APB) to the LOSC24 clock */ @@ -83,11 +103,18 @@ int sunxi_setup_clocks(uint16_t socid) AXI_CLKDIV(3) )); udelay(20); - /* Set to 816MHz, but don't enable yet. */ - mmio_write_32(CCMU_PLL_CPUX_CTRL_REG, PLL_CPUX_816MHZ); + /* Calculate the factors for the target frequency. */ + get_pll1_factors(&freq_mhz, &n, &k, &m); + + /* Build up the register value without the enable bit. */ + reg = (n<<8) | (k<<4) | m; + + /* Set to the target frequency, but don't enable yet. */ + mmio_write_32(CCMU_PLL_CPUX_CTRL_REG, reg); /* Enable PLL_CPUX again */ mmio_setbits32(CCMU_PLL_CPUX_CTRL_REG, PLL_ENABLE_BIT); + /* Wait until the PLL_CPUX becomes stable */ pll_wait_until_stable(CCMU_PLL_CPUX_CTRL_REG); @@ -99,6 +126,19 @@ int sunxi_setup_clocks(uint16_t socid) /* Wait 1000us, because Allwiner does so... */ udelay(1000); +} + +int sunxi_setup_clocks(uint16_t socid) +{ + uint32_t reg; + + /* Avoid reprogramming PERIPH0 if not necessary */ + reg = mmio_read_32(CCMU_PLL_PERIPH0_CTRL_REG); + if ((reg & 0x0fffffff) != 0x41811) /* is not at 600 MHz? */ + mmio_write_32(CCMU_PLL_PERIPH0_CTRL_REG, 0x80041811); + + /* Set CPU speed to 816MHz */ + sun50i_set_cpu_pll(816); /* AHB1 = PERIPH0 / (3 * 1) = 200MHz, APB1 = AHB1 / 2 */ mmio_write_32(CCMU_AHB1_APB1_CFG_REG, 0x00003180); diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h index aefa763..116a38b 100644 --- a/plat/sun50iw1p1/sunxi_private.h +++ b/plat/sun50iw1p1/sunxi_private.h @@ -74,6 +74,7 @@ int sunxi_pmic_write(uint8_t address, uint8_t value); void udelay(unsigned int delay); int sunxi_setup_clocks(uint16_t socid); +void sun50i_set_cpu_pll(unsigned int freq_mhz); /* Gets the SPSR for BL33 entry */ uint32_t sunxi_get_spsr_for_bl33_entry(int aarch); -- cgit v1.2.3