Add support for the "NG" clock bindings for sun5i SoCs.
authorkettenis <kettenis@openbsd.org>
Sun, 3 Mar 2024 21:42:41 +0000 (21:42 +0000)
committerkettenis <kettenis@openbsd.org>
Sun, 3 Mar 2024 21:42:41 +0000 (21:42 +0000)
ok patrick@

sys/dev/fdt/sxiccmu.c
sys/dev/fdt/sxiccmu_clocks.h

index 31d4c7e..afb3966 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sxiccmu.c,v 1.35 2024/02/07 22:00:38 uaa Exp $        */
+/*     $OpenBSD: sxiccmu.c,v 1.36 2024/03/03 21:42:41 kettenis Exp $   */
 /*
  * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
  * Copyright (c) 2013 Artturi Alm
@@ -92,6 +92,8 @@ void  sxiccmu_ccu_reset(void *, uint32_t *, int);
 
 uint32_t sxiccmu_a10_get_frequency(struct sxiccmu_softc *, uint32_t);
 int    sxiccmu_a10_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
+uint32_t sxiccmu_a10s_get_frequency(struct sxiccmu_softc *, uint32_t);
+int    sxiccmu_a10s_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
 uint32_t sxiccmu_a23_get_frequency(struct sxiccmu_softc *, uint32_t);
 int    sxiccmu_a23_set_frequency(struct sxiccmu_softc *, uint32_t, uint32_t);
 uint32_t sxiccmu_a64_get_frequency(struct sxiccmu_softc *, uint32_t);
@@ -139,6 +141,8 @@ sxiccmu_match(struct device *parent, void *match, void *aux)
        }
 
        return (OF_is_compatible(node, "allwinner,sun4i-a10-ccu") ||
+           OF_is_compatible(node, "allwinner,sun5i-a10s-ccu") ||
+           OF_is_compatible(node, "allwinner,sun5i-a13-ccu") ||
            OF_is_compatible(node, "allwinner,sun7i-a20-ccu") ||
            OF_is_compatible(node, "allwinner,sun8i-a23-ccu") ||
            OF_is_compatible(node, "allwinner,sun8i-a23-prcm") ||
@@ -193,6 +197,14 @@ sxiccmu_attach(struct device *parent, struct device *self, void *aux)
                sc->sc_nresets = nitems(sun4i_a10_resets);
                sc->sc_get_frequency = sxiccmu_a10_get_frequency;
                sc->sc_set_frequency = sxiccmu_a10_set_frequency;
+       } else if (OF_is_compatible(node, "allwinner,sun5i-a10s-ccu")) {
+               KASSERT(faa->fa_nreg > 0);
+               sc->sc_gates = sun5i_a10s_gates;
+               sc->sc_ngates = nitems(sun5i_a10s_gates);
+               sc->sc_resets = sun5i_a10s_resets;
+               sc->sc_nresets = nitems(sun5i_a10s_resets);
+               sc->sc_get_frequency = sxiccmu_a10s_get_frequency;
+               sc->sc_set_frequency = sxiccmu_a10s_set_frequency;
        } else if (OF_is_compatible(node, "allwinner,sun8i-a23-ccu") ||
            OF_is_compatible(node, "allwinner,sun8i-a33-ccu")) {
                KASSERT(faa->fa_nreg > 0);
@@ -907,7 +919,7 @@ sxiccmu_ccu_get_frequency(void *cookie, uint32_t *cells)
        return sc->sc_get_frequency(sc, idx);
 }
 
-/* Allwinner A10/A20 */
+/* Allwinner A10/A10s/A13/A20 */
 #define A10_PLL1_CFG_REG               0x0000
 #define A10_PLL1_OUT_EXT_DIVP_MASK     (0x3 << 16)
 #define A10_PLL1_OUT_EXT_DIVP_SHIFT    16
@@ -989,6 +1001,62 @@ sxiccmu_a10_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
        return 0;
 }
 
+uint32_t
+sxiccmu_a10s_get_frequency(struct sxiccmu_softc *sc, uint32_t idx)
+{
+       uint32_t parent;
+       uint32_t reg, div;
+       uint32_t k, m, n, p;
+
+       switch (idx) {
+       case A10S_CLK_LOSC:
+               return clock_get_frequency(sc->sc_node, "losc");
+       case A10S_CLK_HOSC:
+               return clock_get_frequency(sc->sc_node, "hosc");
+       case A10S_CLK_PLL_CORE:
+               reg = SXIREAD4(sc, A10_PLL1_CFG_REG);
+               k = A10_PLL1_FACTOR_K(reg) + 1;
+               m = A10_PLL1_FACTOR_M(reg) + 1;
+               n = A10_PLL1_FACTOR_N(reg);
+               p = 1 << A10_PLL1_OUT_EXT_DIVP(reg);
+               return (24000000 * n * k) / (m * p);
+       case A10S_CLK_PLL_PERIPH:
+               return 1200000000;
+       case A10S_CLK_CPU:
+               reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
+               switch (reg & A10_CPU_CLK_SRC_SEL) {
+               case A10_CPU_CLK_SRC_SEL_LOSC:
+                       parent = A10S_CLK_LOSC;
+                       break;
+               case A10_CPU_CLK_SRC_SEL_OSC24M:
+                       parent = A10S_CLK_HOSC;
+                       break;
+               case A10_CPU_CLK_SRC_SEL_PLL1:
+                       parent = A10S_CLK_PLL_CORE;
+                       break;
+               case A10_CPU_CLK_SRC_SEL_200MHZ:
+                       return 200000000;
+               }
+               return sxiccmu_ccu_get_frequency(sc, &parent);
+       case A10S_CLK_AXI:
+               reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
+               div = 1 << A10_AXI_CLK_DIV_RATIO(reg);
+               parent = A10S_CLK_CPU;
+               return sxiccmu_ccu_get_frequency(sc, &parent) / div;
+       case A10S_CLK_AHB:
+               reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
+               div = 1 << A10_AHB_CLK_DIV_RATIO(reg);
+               parent = A10S_CLK_AXI;
+               return sxiccmu_ccu_get_frequency(sc, &parent) / div;
+       case A10S_CLK_APB1:
+               /* XXX Controlled by a MUX. */
+               return 24000000;
+       }
+
+       printf("%s: 0x%08x\n", __func__, idx);
+       return 0;
+}
+
 /* Allwinner A23/A64/H3/H5/R40 */
 #define CCU_AHB1_APB1_CFG_REG          0x0054
 #define CCU_AHB1_CLK_SRC_SEL           (3 << 12)
@@ -1665,6 +1733,66 @@ sxiccmu_a10_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
        return -1;
 }
 
+int
+sxiccmu_a10s_set_frequency(struct sxiccmu_softc *sc, uint32_t idx,
+    int32_t freq)
+{
+       struct sxiccmu_clock clock;
+       uint32_t parent, parent_freq;
+       uint32_t reg;
+       int k, n;
+       int error;
+
+       switch (idx) {
+       case A10S_CLK_PLL_CORE:
+               k = 1; n = 32;
+               while (k <= 4 && (24000000 * n * k) < freq)
+                       k++;
+               while (n >= 1 && (24000000 * n * k) > freq)
+                       n--;
+
+               reg = SXIREAD4(sc, A10_PLL1_CFG_REG);
+               reg &= ~A10_PLL1_OUT_EXT_DIVP_MASK;
+               reg &= ~A10_PLL1_FACTOR_N_MASK;
+               reg &= ~A10_PLL1_FACTOR_K_MASK;
+               reg &= ~A10_PLL1_FACTOR_M_MASK;
+               reg |= (n << A10_PLL1_FACTOR_N_SHIFT);
+               reg |= ((k - 1) << A10_PLL1_FACTOR_K_SHIFT);
+               SXIWRITE4(sc, A10_PLL1_CFG_REG, reg);
+
+               /* No need to wait PLL to lock? */
+
+               return 0;
+       case A10S_CLK_CPU:
+               /* Switch to 24 MHz clock. */
+               reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
+               reg &= ~A10_CPU_CLK_SRC_SEL;
+               reg |= A10_CPU_CLK_SRC_SEL_OSC24M;
+               SXIWRITE4(sc, A10_CPU_AHB_APB0_CFG_REG, reg);
+
+               error = sxiccmu_a10s_set_frequency(sc, A10S_CLK_PLL_CORE, freq);
+
+               /* Switch back to PLL. */
+               reg = SXIREAD4(sc, A10_CPU_AHB_APB0_CFG_REG);
+               reg &= ~A10_CPU_CLK_SRC_SEL;
+               reg |= A10_CPU_CLK_SRC_SEL_PLL1;
+               SXIWRITE4(sc, A10_CPU_AHB_APB0_CFG_REG, reg);
+               return error;
+       case A10S_CLK_MMC0:
+       case A10S_CLK_MMC1:
+       case A10S_CLK_MMC2:
+               clock.sc_iot = sc->sc_iot;
+               bus_space_subregion(sc->sc_iot, sc->sc_ioh,
+                   sc->sc_gates[idx].reg, 4, &clock.sc_ioh);
+               parent = A10S_CLK_PLL_PERIPH;
+               parent_freq = sxiccmu_ccu_get_frequency(sc, &parent);
+               return sxiccmu_mmc_do_set_frequency(&clock, freq, parent_freq);
+       }
+
+       printf("%s: 0x%08x\n", __func__, idx);
+       return -1;
+}
+
 int
 sxiccmu_a23_set_frequency(struct sxiccmu_softc *sc, uint32_t idx, uint32_t freq)
 {
index bda6855..cce1be6 100644 (file)
@@ -90,6 +90,64 @@ const struct sxiccmu_ccu_bit sun4i_a10_gates[] = {
        [A10_CLK_USB_PHY] =    { 0x00cc, 8 },
 };
 
+/* A10s */
+
+#define A10S_CLK_HOSC          1
+#define A10S_CLK_PLL_CORE      2
+#define A10S_CLK_PLL_PERIPH    14
+
+#define A10S_CLK_CPU           17
+#define A10S_CLK_AXI           18
+#define A10S_CLK_AHB           19
+#define A10S_CLK_APB1          21
+
+#define A10S_CLK_AHB_EHCI0     24
+#define A10S_CLK_AHB_OHCI0     25
+#define A10S_CLK_AHB_MMC0      29
+#define A10S_CLK_AHB_MMC1      30
+#define A10S_CLK_AHB_MMC2      31
+#define A10S_CLK_AHB_EMAC      34
+#define A10S_CLK_APB0_PIO      53
+#define A10S_CLK_APB1_I2C0     56
+#define A10S_CLK_APB1_I2C1     57
+#define A10S_CLK_APB1_I2C2     58
+#define A10S_CLK_APB1_UART0    59
+#define A10S_CLK_APB1_UART1    60
+#define A10S_CLK_APB1_UART2    61
+#define A10S_CLK_APB1_UART3    62
+
+#define A10S_CLK_MMC0          64
+#define A10S_CLK_MMC1          65
+#define A10S_CLK_MMC2          66
+#define A10S_CLK_USB_OHCI0     76
+#define A10S_CLK_USB_PHY0      77
+#define A10S_CLK_USB_PHY1      78
+
+#define A10S_CLK_LOSC          254
+
+const struct sxiccmu_ccu_bit sun5i_a10s_gates[] = {
+       [A10S_CLK_AHB_EHCI0] =  { 0x0060, 1 },
+       [A10S_CLK_AHB_OHCI0] =  { 0x0060, 2 },
+       [A10S_CLK_AHB_MMC0] =   { 0x0060, 8 },
+       [A10S_CLK_AHB_MMC1] =   { 0x0060, 9 },
+       [A10S_CLK_AHB_MMC2] =   { 0x0060, 10 },
+       [A10S_CLK_AHB_EMAC] =   { 0x0060, 17 },
+       [A10S_CLK_APB0_PIO] =   { 0x0068, 5 },
+       [A10S_CLK_APB1_I2C0] =  { 0x006c, 0, A10S_CLK_APB1 },
+       [A10S_CLK_APB1_I2C1] =  { 0x006c, 1, A10S_CLK_APB1 },
+       [A10S_CLK_APB1_I2C2] =  { 0x006c, 2, A10S_CLK_APB1 },
+       [A10S_CLK_APB1_UART0] = { 0x006c, 16, A10S_CLK_APB1 },
+       [A10S_CLK_APB1_UART1] = { 0x006c, 17, A10S_CLK_APB1 },
+       [A10S_CLK_APB1_UART2] = { 0x006c, 18, A10S_CLK_APB1 },
+       [A10S_CLK_APB1_UART3] = { 0x006c, 19, A10S_CLK_APB1 },
+       [A10S_CLK_MMC0] =       { 0x0088, 31 },
+       [A10S_CLK_MMC1] =       { 0x008c, 31 },
+       [A10S_CLK_MMC2] =       { 0x0090, 31 },
+       [A10S_CLK_USB_OHCI0] =  { 0x00cc, 6 },
+       [A10S_CLK_USB_PHY0] =   { 0x00cc, 8 },
+       [A10S_CLK_USB_PHY1] =   { 0x00cc, 9 },
+};
+
 /* A23/A33 */
 
 #define A23_CLK_PLL_PERIPH     10
@@ -773,6 +831,16 @@ const struct sxiccmu_ccu_bit sun4i_a10_resets[] = {
        [A10_RST_USB_PHY2] = { 0x00cc, 2 },
 };
 
+/* A10s */
+
+#define A10S_RST_USB_PHY0      0
+#define A10S_RST_USB_PHY1      1
+
+const struct sxiccmu_ccu_bit sun5i_a10s_resets[] = {
+       [A10S_RST_USB_PHY0] = { 0x00cc, 0 },
+       [A10S_RST_USB_PHY1] = { 0x00cc, 1 },
+};
+
 /* A23/A33 */
 
 #define A23_RST_USB_PHY0       0