summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cpu/allwinner/a10/clock.c81
-rw-r--r--src/cpu/allwinner/a10/clock.h28
2 files changed, 109 insertions, 0 deletions
diff --git a/src/cpu/allwinner/a10/clock.c b/src/cpu/allwinner/a10/clock.c
index 87a3574783..0401a72be4 100644
--- a/src/cpu/allwinner/a10/clock.c
+++ b/src/cpu/allwinner/a10/clock.c
@@ -9,6 +9,8 @@
#include <arch/io.h>
+static struct a10_ccm *const ccm = (void *)A1X_CCM_BASE;
+
/**
* \brief Enable the clock source for the peripheral
*
@@ -40,3 +42,82 @@ void a1x_periph_clock_disable(enum a1x_clken periph)
reg32 &= ~(1 << (periph & 0x1f));
write32(reg32, addr);
}
+
+/**
+ * \brief Configure PLL5 factors
+ *
+ * This is a low-level accessor to configure the divisors and multipliers of
+ * PLL5. PLL5 uses two factors to multiply the 24MHz oscillator clock to
+ * generate a pre-clock. The pre-divided clock is then divided by one of two
+ * independent divisors, one for DRAM, and another for peripherals clocked from
+ * this PLL. If the PLL was previously disabled, this function will enable it.
+ * Other than that, this function only modifies these factors, and leaves the
+ * other settings unchanged.
+ *
+ * The output clocks are given by the following formulas:
+ *
+ * Pre-clock = (24 MHz * N * K) <- Must be between 240MHz and 2GHz
+ * DRAM clock = pre / M
+ * Other module = pre / P
+ *
+ * It is the caller's responsibility to make sure the pre-divided clock falls
+ * within the operational range of the PLL, and that the divisors and
+ * multipliers are within their ranges.
+ *
+ * @param[in] mul_n Multiplier N, between 0 and 32
+ * @param[in] mul_k Multiplier K, between 1 and 4
+ * @param[in] div_m DRAM clock divisor, between 1 and 4
+ * @param[in] exp_div_p Peripheral clock divisor exponent, between 0 and 3
+ * (P = 1/2/4/8, respectively)
+ */
+void a1x_pll5_configure(u8 mul_n, u8 mul_k, u8 div_m, u8 exp_div_p)
+{
+ u32 reg32;
+
+ reg32 = read32(&ccm->pll5_cfg);
+ reg32 &= ~(PLL5_FACTOR_M_MASK | PLL5_FACTOR_N_MASK |
+ PLL5_FACTOR_K_MASK | PLL5_DIV_EXP_P_MASK);
+ /* The M1 factor is not documented in the datasheet, and the reference
+ * raminit code does not use it. Whether this is a fractional divisor,
+ * or an additional divisor is unknown, so don't use it for now */
+ reg32 &= ~PLL5_FACTOR_M1_MASK;
+ reg32 |= (PLL5_FACTOR_M(div_m) | PLL5_FACTOR_N(mul_n) |
+ PLL5_FACTOR_K(mul_k) | PLL5_DIV_EXP_P(exp_div_p));
+ reg32 |= PLL5_PLL_ENABLE;
+ write32(reg32, &ccm->pll5_cfg);
+}
+
+/**
+ * \brief Enable the clock output to DRAM chips
+ *
+ * This enables the DRAM clock to be sent to DRAM chips. This should normally be
+ * done after PLL5 is configured and locked. Note that the clock may be gated,
+ * and also needs to be ungated in order to reach the DDR chips.
+ * Also see @ref clock_ungate_dram_clk_output
+ */
+void a1x_pll5_enable_dram_clock_output(void)
+{
+ setbits_le32(&ccm->pll5_cfg, PLL5_DDR_CLK_OUT_EN);
+}
+
+/**
+ * \brief Ungate the clock to DRAM chips
+ *
+ * Although the DRAM clock output may be enabled, it is by default gated. It
+ * needs to be ungated before reaching DRAM.
+ */
+void a1x_ungate_dram_clock_output(void)
+{
+ setbits_le32(&ccm->dram_clk_cfg, DRAM_CTRL_DCLK_OUT);
+}
+
+/**
+ * \brief Gate the clock to DRAM chips
+ *
+ * Disable the clock to DRAM without altering PLL configuration, by closing the
+ * DRAM clock gate.
+ */
+void a1x_gate_dram_clock_output(void)
+{
+ clrbits_le32(&ccm->dram_clk_cfg, DRAM_CTRL_DCLK_OUT);
+}
diff --git a/src/cpu/allwinner/a10/clock.h b/src/cpu/allwinner/a10/clock.h
index 96564694db..41400abb7e 100644
--- a/src/cpu/allwinner/a10/clock.h
+++ b/src/cpu/allwinner/a10/clock.h
@@ -43,6 +43,29 @@
#define APB1_RAT_M_MASK 0x1f << 0)
#define APB1_RAT_M(n) (((n) & 0x1f) << 0)
+/* PLL5_CFG values */
+#define PLL5_PLL_ENABLE (1 << 31)
+#define PLL5_OUT_BYPASS_EN (1 << 30)
+#define PLL5_DDR_CLK_OUT_EN (1 << 29)
+#define PLL5_DIV_EXP_P_MASK (0x3 << 16)
+#define PLL5_DIV_EXP_P(ep) ((ep << 16) & PLL5_DIV_EXP_P_MASK)
+#define PLL5_DIV_P_1 (0x0 << 16)
+#define PLL5_DIV_P_2 (0x1 << 16)
+#define PLL5_DIV_P_4 (0x2 << 16)
+#define PLL5_DIV_P_8 (0x3 << 16)
+#define PLL5_FACTOR_N_MASK (0x1f << 8)
+#define PLL5_FACTOR_N(n) ((n << 8) & PLL5_FACTOR_N_MASK)
+#define PLL5_LDO_EN (1 << 7)
+#define PLL5_FACTOR_K_MASK (0x3 << 4)
+#define PLL5_FACTOR_K(k) ((((k) - 1) << 4) & PLL5_FACTOR_K_MASK)
+#define PLL5_FACTOR_M1_MASK (0x3 << 2)
+#define PLL5_FACTOR_M1(m1) (((m1) << 2) & PLL5_FACTOR_M1_MASK)
+#define PLL5_FACTOR_M_MASK (0x3 << 0)
+#define PLL5_FACTOR_M(m) ((((m) - 1) << 0) & PLL5_FACTOR_M_MASK)
+
+/* DRAM_CLK values*/
+#define DRAM_CTRL_DCLK_OUT (1 << 15)
+
/**
* \brief Clock gating definitions
*
@@ -228,4 +251,9 @@ struct a10_ccm {
void a1x_periph_clock_enable(enum a1x_clken periph);
void a1x_periph_clock_disable(enum a1x_clken periph);
+void a1x_pll5_configure(u8 mul_n, u8 mul_k, u8 div_m, u8 exp_div_p);
+void a1x_pll5_enable_dram_clock_output(void);
+void a1x_ungate_dram_clock_output(void);
+void a1x_gate_dram_clock_output(void);
+
#endif /* CPU_ALLWINNER_A10_CLOCK_H */