-/* $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
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);
}
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") ||
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);
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
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)
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)
{
[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
[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