summaryrefslogtreecommitdiff
path: root/plat/sun50iw1p1
diff options
context:
space:
mode:
authorPhilipp Tomsich <philipp.tomsich@theobroma-systems.com>2017-02-02 23:47:22 +0100
committerPhilipp Tomsich <philipp.tomsich@theobroma-systems.com>2017-02-02 23:50:42 +0100
commite0d77b508aaf5fe833df755097959dd98053d0e0 (patch)
tree5a7980faa740a13fba5b8cdecf907851533a60f2 /plat/sun50iw1p1
parentfb2b98e5422f30aec64f43f4cf9bcf22c5736719 (diff)
sun50iw1p1: Adjust clock initialisation to follow Allwinner's guidance
Initialisation of clocks on Allwinner's CPUs has always been a bit tricky and should follow the following guidance: 1. Bus clock dividers should be adjusted first to keep the bus clocks within their operating limits for both the new frequency _before_ changing the PLL (compare to section 3.3.6.2. in the A64 User's Guide v1.0). 2. PLLs should first be setup (with the enable-bit cleared), then be enabled and finally polled for the stable-bit to indicate the a PLL lock (compare how boot0 and Allwinner's linux releases have been changing PLLs for the A31 and subsequent chips). 3. Additionally Allwinner always injects extra delays after the PLL lock has triggered and after the clock source is changed. Without these changes, the A64 will not reliably come up beyond the clock initialisation w/ the recurrence of failure differing between individual parts (i.e. seemingly process-dependent). Note, that these changes and the observed failures are in line with our experience on the A31 and A80. X-AffectedPlatforms: A64-uQ7 Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
Diffstat (limited to 'plat/sun50iw1p1')
-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);