summaryrefslogtreecommitdiff
path: root/plat/sun50iw1p1/sunxi_clocks.c
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/sunxi_clocks.c
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/sunxi_clocks.c')
-rw-r--r--plat/sun50iw1p1/sunxi_clocks.c70
1 files changed, 53 insertions, 17 deletions
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);