summaryrefslogtreecommitdiff
path: root/plat
diff options
context:
space:
mode:
Diffstat (limited to 'plat')
-rw-r--r--plat/sun50iw1p1/include/ccmu.h16
-rw-r--r--plat/sun50iw1p1/sunxi_clocks.c70
2 files changed, 69 insertions, 17 deletions
diff --git a/plat/sun50iw1p1/include/ccmu.h b/plat/sun50iw1p1/include/ccmu.h
index a1e817f..e1d1c05 100644
--- a/plat/sun50iw1p1/include/ccmu.h
+++ b/plat/sun50iw1p1/include/ccmu.h
@@ -32,6 +32,7 @@
#define CCMU_PLL_VE_CTRL_REG (SUNXI_CCM_BASE + 0x18)
#define CCMU_PLL_DDR0_CTRL_REG (SUNXI_CCM_BASE + 0x20)
#define CCMU_PLL_PERIPH0_CTRL_REG (SUNXI_CCM_BASE + 0x28)
+
#define CCMU_PLL_PERIPH1_CTRL_REG (SUNXI_CCM_BASE + 0x2C)
#define CCMU_PLL_VIDEO1_CTRL_REG (SUNXI_CCM_BASE + 0x30)
#define CCMU_PLL_GPU_CTRL_REG (SUNXI_CCM_BASE + 0x38)
@@ -40,8 +41,20 @@
#define CCMU_PLL_DE_CTRL_REG (SUNXI_CCM_BASE + 0x48)
#define CCMU_PLL_DDR1_CTRL_REG (SUNXI_CCM_BASE + 0x4C)
+#define PLL_ENABLE_BIT (1 << 31)
+#define PLL_STABLE_BIT (1 << 28)
+
/* cfg list */
#define CCMU_CPUX_AXI_CFG_REG (SUNXI_CCM_BASE + 0x50)
+#define CPUX_SRCSEL_MASK (0x3 << 16)
+#define CPUX_SRCSEL_LOSC (0x0 << 16)
+#define CPUX_SRCSEL_OSC24M (0x1 << 16)
+#define CPUX_SRCSEL_PLLCPUX (0x2 << 16)
+#define AXI_CLKDIV_MASK (0x3 << 8)
+#define AXI_CLKDIV(n) ((n-1) << 8)
+#define APB_CLKDIV_MASK (0x3 << 0)
+#define APB_CLKDIV(n) ((n-1) << 0)
+
#define CCMU_AHB1_APB1_CFG_REG (SUNXI_CCM_BASE + 0x54)
#define CCMU_APB2_CFG_GREG (SUNXI_CCM_BASE + 0x58)
#define CCMU_AHB2_CFG_GREG (SUNXI_CCM_BASE + 0x5C)
@@ -77,5 +90,8 @@
#define CCMU_BUS_SOFT_RST_REG3 (SUNXI_CCM_BASE + 0x2D0)
#define CCMU_BUS_SOFT_RST_REG4 (SUNXI_CCM_BASE + 0x2D8)
+#define CCMU_PLL_LOCK_CTRL_REG (SUNXI_CCM_BASE + 0x320)
+#define LOCK_EN_PLL_CPUX (1<<0)
+
#endif
diff --git a/plat/sun50iw1p1/sunxi_clocks.c b/plat/sun50iw1p1/sunxi_clocks.c
index a071024..1f8a002 100644
--- a/plat/sun50iw1p1/sunxi_clocks.c
+++ b/plat/sun50iw1p1/sunxi_clocks.c
@@ -33,36 +33,72 @@
#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);
+ regval &= ~mask;
+ regval |= bits;
+ mmio_write_32(addr, regval);
+}
+
+
+static void mmio_setbits32(uintptr_t addr, uint32_t bits)
+{
+ uint32_t regval = mmio_read_32(addr);
+ regval |= bits;
+ mmio_write_32(addr, regval);
+}
+
+/* TODO (prt): we should have a timeout and return an error/success... */
+static int pll_wait_until_stable(uintptr_t addr)
+{
+ while ((mmio_read_32(addr) & PLL_STABLE_BIT) != PLL_STABLE_BIT) {
+ /* spin */
+ }
+
+ return 0;
+}
+
int sunxi_setup_clocks(uint16_t socid)
{
uint32_t reg;
+ NOTICE("BL3-1: Reprogramming clocks for a %d MHz\n");
+
/* 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);
- /* Check initial CPU frequency: */
- reg = mmio_read_32(CCMU_PLL_CPUX_CTRL_REG);
+ /* Set up dividers (suitable for the target clock frequency)
+ and switch CPUX (and thus AXI & APB) to the LOSC24 clock */
+ mmio_write_32(CCMU_CPUX_AXI_CFG_REG, ( CPUX_SRCSEL_OSC24M |
+ APB_CLKDIV(4) |
+ AXI_CLKDIV(3) ));
+ udelay(20);
- if (socid == 0x1689) {
- if ((reg & 0x0fffffff) != 0x1010) { /* if not at 816 MHz: */
- /* switch CPU to 24 MHz source for changing PLL1 */
- mmio_write_32(CCMU_CPUX_AXI_CFG_REG, 0x00010000);
- udelay(1);
+ /* Set to 816MHz, but don't enable yet. */
+ mmio_write_32(CCMU_PLL_CPUX_CTRL_REG, PLL_CPUX_816MHZ);
- /* Set to 816 MHz */
- mmio_write_32(CCMU_PLL_CPUX_CTRL_REG, 0x80001010);
- udelay(1);
- }
+ /* 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);
- /* switch CPU to PLL1 source, AXI = CPU/3, APB = CPU/4 */
- mmio_write_32(CCMU_CPUX_AXI_CFG_REG, 0x00020302);
- udelay(1);
+ /* Wait another 20us, because Allwinner does so... */
+ udelay(20);
- } else {
- NOTICE("PLL_CPUX: %x\n", reg);
- }
+ /* Switch AXI clock back to PLL_CPUX, dividers have already been set up */
+ mmio_clrsetbits32(CCMU_CPUX_AXI_CFG_REG, CPUX_SRCSEL_MASK, CPUX_SRCSEL_PLLCPUX);
+
+ /* Wait 1000us, because Allwiner does so... */
+ udelay(1000);
/* AHB1 = PERIPH0 / (3 * 1) = 200MHz, APB1 = AHB1 / 2 */
mmio_write_32(CCMU_AHB1_APB1_CFG_REG, 0x00003180);