summaryrefslogtreecommitdiff
path: root/plat/sun50iw1p1/sunxi_clocks.c
diff options
context:
space:
mode:
Diffstat (limited to 'plat/sun50iw1p1/sunxi_clocks.c')
-rw-r--r--plat/sun50iw1p1/sunxi_clocks.c70
1 files changed, 55 insertions, 15 deletions
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 <ccmu.h>
#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);