diff options
author | SASANO Takayoshi <uaa@cvs.openbsd.org> | 2020-08-30 05:23:50 +0000 |
---|---|---|
committer | SASANO Takayoshi <uaa@cvs.openbsd.org> | 2020-08-30 05:23:50 +0000 |
commit | 9ab1a01cffcbfc1529c11e1069697833123223aa (patch) | |
tree | 2c034ae39c0e0410dbef103fadf0bfaecb663aca | |
parent | cfd1f4e78a96320e593e0f3fb1c4646224b8ec90 (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.c | 27 |
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: |