summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSASANO Takayoshi <uaa@cvs.openbsd.org>2020-08-30 05:23:50 +0000
committerSASANO Takayoshi <uaa@cvs.openbsd.org>2020-08-30 05:23:50 +0000
commit9ab1a01cffcbfc1529c11e1069697833123223aa (patch)
tree2c034ae39c0e0410dbef103fadf0bfaecb663aca
parentcfd1f4e78a96320e593e0f3fb1c4646224b8ec90 (diff)
PLL1(CPU_PLL) stability improvement for Allwinner H3/H2+
Due to unstable of PLL1, sometimes the system has hanged up especially at boot. This is observed at Allwinner H3/H2+ processor. To solve the problem, PLL1 setting procedure is same as Linux. 1. change clock source to 24MHz 2. wait 1usec (new) 3. disable PLL1 (new) 4. set new NKMP value, but M should be 1 5. re-enable PLL1 (new) 6. wait PLL1 stable (modified) 7. change clock source to PLL1 8. wait 1usec (new) Once disable PLL1 before setting NKMP is very important. And, sometimes LOCK flag is set even if PLL has not locked yet so wait for PLL is modified with simple delay() by the value of PLL_STABLE_TIME_REG1 register. Not only Allwinner H3/H2+ but also all (i.e. A64) Allwinner processors datasheet has "If the clock source is changed, at most to wait for 8 present running clock cycles." sentence at CPU clock source selection field of CPU/AXI configuration register. But this is ambiguous that _who_ should do _what_ during that cycles. It is unclear that changing clock source itself invoke PLL1 unstability. For safety, added 1usec wait after changing clock source like Linux. ok by kettenis@, thanks to adr at sdf dot org
-rw-r--r--sys/dev/fdt/sxiccmu.c27
1 files changed, 21 insertions, 6 deletions
diff --git a/sys/dev/fdt/sxiccmu.c b/sys/dev/fdt/sxiccmu.c
index 0fbc9c69dcd..31a8f4a19a4 100644
--- a/sys/dev/fdt/sxiccmu.c
+++ b/sys/dev/fdt/sxiccmu.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sxiccmu.c,v 1.27 2020/03/28 12:32:53 kettenis Exp $ */
+/* $OpenBSD: sxiccmu.c,v 1.28 2020/08/30 05:23:49 uaa Exp $ */
/*
* Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
* Copyright (c) 2013 Artturi Alm
@@ -1158,6 +1158,7 @@ sxiccmu_a80_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
/* Allwinner H3/H5 */
#define H3_PLL_CPUX_CTRL_REG 0x0000
+#define H3_PLL_CPUX_ENABLE (1 << 31)
#define H3_PLL_CPUX_LOCK (1 << 28)
#define H3_PLL_CPUX_OUT_EXT_DIVP(x) (((x) >> 16) & 0x3)
#define H3_PLL_CPUX_OUT_EXT_DIVP_MASK (0x3 << 16)
@@ -1176,6 +1177,8 @@ sxiccmu_a80_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
#define H3_CPUX_CLK_SRC_SEL_LOSC (0x0 << 16)
#define H3_CPUX_CLK_SRC_SEL_OSC24M (0x1 << 16)
#define H3_CPUX_CLK_SRC_SEL_PLL_CPUX (0x2 << 16)
+#define H3_PLL_STABLE_TIME_REG1 0x0204
+#define H3_PLL_STABLE_TIME_REG1_TIME(x) (((x) >> 0) & 0xffff)
uint32_t
sxiccmu_h3_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
@@ -1632,7 +1635,7 @@ sxiccmu_h3_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
{
struct sxiccmu_clock clock;
uint32_t parent, parent_freq;
- uint32_t reg;
+ uint32_t reg, lock_time;
int k, n;
int error;
@@ -1644,7 +1647,12 @@ sxiccmu_h3_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
while (n >= 1 && (24000000 * n * k) > freq)
n--;
+ /* Gate the PLL first */
reg = SXIREAD4(sc, H3_PLL_CPUX_CTRL_REG);
+ reg &= ~H3_PLL_CPUX_ENABLE;
+ SXIWRITE4(sc, H3_PLL_CPUX_CTRL_REG, reg);
+
+ /* Set factors and external divider. */
reg &= ~H3_PLL_CPUX_OUT_EXT_DIVP_MASK;
reg &= ~H3_PLL_CPUX_FACTOR_N_MASK;
reg &= ~H3_PLL_CPUX_FACTOR_K_MASK;
@@ -1653,10 +1661,13 @@ sxiccmu_h3_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
reg |= ((k - 1) << H3_PLL_CPUX_FACTOR_K_SHIFT);
SXIWRITE4(sc, H3_PLL_CPUX_CTRL_REG, reg);
- /* Wait for PLL to lock. */
- while ((SXIREAD4(sc, H3_PLL_CPUX_CTRL_REG) &
- H3_PLL_CPUX_LOCK) == 0)
- delay(1);
+ /* Ungate the PLL */
+ reg |= H3_PLL_CPUX_ENABLE;
+ SXIWRITE4(sc, H3_PLL_CPUX_CTRL_REG, reg);
+
+ /* Wait for PLL to lock. (LOCK flag is unreliable) */
+ lock_time = SXIREAD4(sc, H3_PLL_STABLE_TIME_REG1);
+ delay(H3_PLL_STABLE_TIME_REG1_TIME(lock_time));
return 0;
case H3_CLK_CPUX:
@@ -1665,6 +1676,8 @@ sxiccmu_h3_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
reg &= ~H3_CPUX_CLK_SRC_SEL;
reg |= H3_CPUX_CLK_SRC_SEL_OSC24M;
SXIWRITE4(sc, H3_CPUX_AXI_CFG_REG, reg);
+ /* Must wait at least 8 cycles of the current clock. */
+ delay(1);
error = sxiccmu_h3_set_frequency(sc, H3_CLK_PLL_CPUX, freq);
@@ -1673,6 +1686,8 @@ sxiccmu_h3_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
reg &= ~H3_CPUX_CLK_SRC_SEL;
reg |= H3_CPUX_CLK_SRC_SEL_PLL_CPUX;
SXIWRITE4(sc, H3_CPUX_AXI_CFG_REG, reg);
+ /* Must wait at least 8 cycles of the current clock. */
+ delay(1);
return error;
case H3_CLK_MMC0:
case H3_CLK_MMC1: