-/* $OpenBSD: stfclock.c,v 1.3 2022/12/28 11:20:09 kettenis Exp $ */
+/* $OpenBSD: stfclock.c,v 1.4 2023/06/24 18:26:59 jsing Exp $ */
/*
* Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
+ * Copyright (c) 2023 Joel Sing <jsing@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
+#include <sys/malloc.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
+#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/fdt.h>
-/* Clock IDs */
+/* JH7100 Clock IDs */
#define JH7100_CLK_CPUNDBUS_ROOT 0
#define JH7100_CLK_GMACUSB_ROOT 3
#define JH7100_CLK_PERH0_ROOT 4
#define JH7100_CLK_OSC_SYS 255
#define JH7100_CLK_OSC_AUD 254
+/* JH7110 Clock IDs */
+#define JH7110_CLK_PLL0_OUT 0
+#define JH7110_CLK_PLL1_OUT 1
+#define JH7110_CLK_PLL2_OUT 2
+
+#define JH7110_SYSCLK_CPU_ROOT 0
+#define JH7110_SYSCLK_CPU_CORE 1
+#define JH7110_SYSCLK_CPU_BUS 2
+#define JH7110_SYSCLK_BUS_ROOT 5
+#define JH7110_SYSCLK_AXI_CFG0 7
+#define JH7110_SYSCLK_STG_AXIAHB 8
+#define JH7110_SYSCLK_AHB0 9
+#define JH7110_SYSCLK_AHB1 10
+#define JH7110_SYSCLK_APB_BUS 11
+#define JH7110_SYSCLK_SDIO0_AHB 91
+#define JH7110_SYSCLK_SDIO1_AHB 92
+#define JH7110_SYSCLK_SDIO0_SDCARD 93
+#define JH7110_SYSCLK_SDIO1_SDCARD 94
+#define JH7110_SYSCLK_TEMP_APB 129
+#define JH7110_SYSCLK_TEMP_CORE 130
+#define JH7110_SYSCLK_UART0_CORE 146
+
+#define JH7110_SYSCLK_OSC 190
+#define JH7110_SYSCLK_PLL0_OUT 199
+#define JH7110_SYSCLK_PLL1_OUT 200
+#define JH7110_SYSCLK_PLL2_OUT 201
+
+#define JH7110_SYSCLK_ASSERT_OFFSET 0x2f8
+#define JH7110_SYSCLK_STATUS_OFFSET 0x308
+
/* Registers */
#define CLKMUX_MASK 0x03000000
#define CLKMUX_SHIFT 24
int sc_node;
struct clock_device sc_cd;
+ struct reset_device sc_rd;
};
int stfclock_match(struct device *, void *, void *);
NULL, "stfclock", DV_DULL
};
-uint32_t stfclock_get_frequency(void *, uint32_t *);
-int stfclock_set_frequency(void *, uint32_t *, uint32_t);
-void stfclock_enable(void *, uint32_t *, int);
+uint32_t stfclock_get_frequency_jh7100(void *, uint32_t *);
+int stfclock_set_frequency_jh7100(void *, uint32_t *, uint32_t);
+void stfclock_enable_jh7100(void *, uint32_t *, int);
+
+uint32_t stfclock_get_frequency_jh7110_pll(void *, uint32_t *);
+int stfclock_set_frequency_jh7110_pll(void *, uint32_t *, uint32_t);
+void stfclock_enable_jh7110_pll(void *, uint32_t *, int);
+
+uint32_t stfclock_get_frequency_jh7110_sys(void *, uint32_t *);
+int stfclock_set_frequency_jh7110_sys(void *, uint32_t *, uint32_t);
+void stfclock_enable_jh7110_sys(void *, uint32_t *, int);
+void stfclock_reset_jh7110_sys(void *, uint32_t *, int);
int
stfclock_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
- return OF_is_compatible(faa->fa_node, "starfive,jh7100-clkgen");
+ return OF_is_compatible(faa->fa_node, "starfive,jh7100-clkgen") ||
+ OF_is_compatible(faa->fa_node, "starfive,jh7110-pll") ||
+ OF_is_compatible(faa->fa_node, "starfive,jh7110-syscrg");
}
void
struct stfclock_softc *sc = (struct stfclock_softc *)self;
struct fdt_attach_args *faa = aux;
- if (faa->fa_nreg < 1) {
- printf(": no registers\n");
- return;
- }
-
- sc->sc_iot = faa->fa_iot;
- if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
- faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
- printf(": can't map registers\n");
- return;
+ if (faa->fa_nreg >= 1) {
+ sc->sc_iot = faa->fa_iot;
+ if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
+ faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
+ printf(": can't map registers\n");
+ return;
+ }
}
sc->sc_node = faa->fa_node;
- printf("\n");
-
sc->sc_cd.cd_node = faa->fa_node;
sc->sc_cd.cd_cookie = sc;
- sc->sc_cd.cd_get_frequency = stfclock_get_frequency;
- sc->sc_cd.cd_set_frequency = stfclock_set_frequency;
- sc->sc_cd.cd_enable = stfclock_enable;
+
+ if (OF_is_compatible(faa->fa_node, "starfive,jh7100-clkgen")) {
+ sc->sc_cd.cd_get_frequency = stfclock_get_frequency_jh7100;
+ sc->sc_cd.cd_set_frequency = stfclock_set_frequency_jh7100;
+ sc->sc_cd.cd_enable = stfclock_enable_jh7100;
+ printf("\n");
+ } else if (OF_is_compatible(faa->fa_node, "starfive,jh7110-pll")) {
+ sc->sc_cd.cd_get_frequency = stfclock_get_frequency_jh7110_pll;
+ sc->sc_cd.cd_set_frequency = stfclock_set_frequency_jh7110_pll;
+ sc->sc_cd.cd_enable = stfclock_enable_jh7110_pll;
+ printf(": pll\n");
+ } else if (OF_is_compatible(faa->fa_node, "starfive,jh7110-syscrg")) {
+ sc->sc_cd.cd_get_frequency = stfclock_get_frequency_jh7110_sys;
+ sc->sc_cd.cd_set_frequency = stfclock_set_frequency_jh7110_sys;
+ sc->sc_cd.cd_enable = stfclock_enable_jh7110_sys;
+
+ sc->sc_rd.rd_node = sc->sc_node;
+ sc->sc_rd.rd_cookie = sc;
+ sc->sc_rd.rd_reset = stfclock_reset_jh7110_sys;
+ reset_register(&sc->sc_rd);
+
+ printf(": syscrg\n");
+ }
+
+ KASSERT(sc->sc_cd.cd_get_frequency);
+
clock_register(&sc->sc_cd);
}
uint32_t
-stfclock_get_frequency(void *cookie, uint32_t *cells)
+stfclock_get_frequency_jh7100(void *cookie, uint32_t *cells)
{
struct stfclock_softc *sc = cookie;
uint32_t idx = cells[0];
case JH7100_CLK_PLL0_OUT:
parent = JH7100_CLK_OSC_SYS;
- return 40 * stfclock_get_frequency(sc, &parent);
+ return 40 * stfclock_get_frequency_jh7100(sc, &parent);
case JH7100_CLK_PLL1_OUT:
parent = JH7100_CLK_OSC_SYS;
- return 64 * stfclock_get_frequency(sc, &parent);
+ return 64 * stfclock_get_frequency_jh7100(sc, &parent);
case JH7100_CLK_PLL2_OUT:
parent = JH7100_CLK_PLL2_REF;
- return 55 * stfclock_get_frequency(sc, &parent);
+ return 55 * stfclock_get_frequency_jh7100(sc, &parent);
}
reg = HREAD4(sc, idx * 4);
parent = JH7100_CLK_PLL2_OUT;
break;
}
- return stfclock_get_frequency(sc, &parent);
+ return stfclock_get_frequency_jh7100(sc, &parent);
case JH7100_CLK_GMACUSB_ROOT:
switch (mux) {
default:
parent = JH7100_CLK_PLL2_OUT;
break;
}
- return stfclock_get_frequency(sc, &parent);
+ return stfclock_get_frequency_jh7100(sc, &parent);
case JH7100_CLK_PERH0_ROOT:
mux = (reg >> 24) & 1;
parent = mux ? JH7100_CLK_PLL0_OUT : JH7100_CLK_OSC_SYS;
- return stfclock_get_frequency(sc, &parent);
+ return stfclock_get_frequency_jh7100(sc, &parent);
case JH7100_CLK_PERH1_ROOT:
mux = (reg >> 24) & 1;
parent = mux ? JH7100_CLK_PLL2_OUT : JH7100_CLK_OSC_SYS;
- return stfclock_get_frequency(sc, &parent);
+ return stfclock_get_frequency_jh7100(sc, &parent);
case JH7100_CLK_PLL2_REF:
parent = mux ? JH7100_CLK_OSC_AUD : JH7100_CLK_OSC_SYS;
- return stfclock_get_frequency(sc, &parent);
+ return stfclock_get_frequency_jh7100(sc, &parent);
}
switch (idx) {
parent = JH7100_CLK_GMAC_ROOT_DIV;
break;
default:
- printf("%s: 0x%08x\n", __func__, idx);
+ printf("%s: unknown clock 0x%08x\n", __func__, idx);
return 0;
}
- freq = stfclock_get_frequency(sc, &parent);
+ freq = stfclock_get_frequency_jh7100(sc, &parent);
return freq / div;
}
int
-stfclock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
+stfclock_set_frequency_jh7100(void *cookie, uint32_t *cells, uint32_t freq)
{
uint32_t idx = cells[0];
- printf("%s: 0x%08x\n", __func__, idx);
+ printf("%s: not handled 0x%08x (freq=0x%08x)\n", __func__, idx, freq);
+
return -1;
}
-
+
void
-stfclock_enable(void *cookie, uint32_t *cells, int on)
+stfclock_enable_jh7100(void *cookie, uint32_t *cells, int on)
{
struct stfclock_softc *sc = cookie;
uint32_t idx = cells[0];
return;
}
- printf("%s: 0x%08x\n", __func__, idx);
+ printf("%s: unknown clock 0x%08x\n", __func__, idx);
+}
+
+uint32_t
+stfclock_get_frequency_jh7110_pll(void *cookie, uint32_t *cells)
+{
+ struct stfclock_softc *sc = cookie;
+ uint32_t idx = cells[0];
+ uint32_t dacpd, dsmpd, fbdiv, frac, prediv, postdiv1, reg;
+ uint64_t frac_val, parent_freq;
+ struct regmap *pllrm;
+ int syscon_node;
+ bus_size_t base;
+
+ parent_freq = clock_get_frequency_idx(sc->sc_node, 0);
+ if (parent_freq == 0) {
+ printf("%s: failed to get parent frequency\n", __func__);
+ return 0;
+ }
+
+ switch (idx) {
+ case JH7110_CLK_PLL0_OUT:
+ base = 0x18;
+ break;
+ case JH7110_CLK_PLL1_OUT:
+ base = 0x24;
+ break;
+ case JH7110_CLK_PLL2_OUT:
+ base = 0x2c;
+ break;
+ default:
+ printf("%s: unknown clock 0x08%x\n", __func__, idx);
+ return 0;
+ }
+
+ syscon_node = OF_parent(sc->sc_node);
+ if (syscon_node < 0) {
+ printf("%s: failed to find pll clock parent\n", __func__);
+ return 0;
+ }
+ pllrm = regmap_bynode(syscon_node);
+ if (pllrm == NULL) {
+ printf("%s: failed to get pll regmap\n", __func__);
+ return 0;
+ }
+
+ switch (idx) {
+ case JH7110_CLK_PLL0_OUT:
+ reg = regmap_read_4(pllrm, base + 0);
+ dacpd = (reg >> 24) & 1;
+ dsmpd = (reg >> 25) & 1;
+
+ reg = regmap_read_4(pllrm, base + 4);
+ fbdiv = (reg >> 0) & ((1UL << 12) - 1);
+
+ reg = regmap_read_4(pllrm, base + 8);
+ frac = (reg >> 0) & ((1UL << 24) - 1);
+ postdiv1 = 1 << ((reg >> 28) & ((1UL << 2) - 1));
+
+ reg = regmap_read_4(pllrm, base + 12);
+ prediv = (reg >> 0) & ((1UL << 6) - 1);
+ break;
+
+ case JH7110_CLK_PLL1_OUT:
+ case JH7110_CLK_PLL2_OUT:
+ reg = regmap_read_4(pllrm, base + 0);
+ dacpd = (reg >> 15) & 1;
+ dsmpd = (reg >> 16) & 1;
+ fbdiv = (reg >> 17) & ((1UL << 12) - 1);
+
+ reg = regmap_read_4(pllrm, base + 4);
+ frac = (reg >> 0) & ((1UL << 24) - 1);
+ postdiv1 = 1 << ((reg >> 28) & ((1UL << 2) - 1));
+
+ reg = regmap_read_4(pllrm, base + 8);
+ prediv = (reg >> 0) & ((1UL << 6) - 1);
+ break;
+ }
+
+ if (fbdiv == 0 || prediv == 0 || postdiv1 == 0) {
+ printf("%s: zero divisor\n", __func__);
+ return 0;
+ }
+
+ if (dacpd != dsmpd)
+ return 0;
+
+ /* Integer mode (dacpd/dsmpd both 0) or fraction mode (both 1). */
+ frac_val = 0;
+ if (dacpd == 0 && dsmpd == 0)
+ frac_val = ((uint64_t)frac * 1000) / (1 << 24);
+
+ return parent_freq / 1000 * (fbdiv * 1000 + frac_val) / prediv / postdiv1;
+}
+
+int
+stfclock_set_frequency_jh7110_pll(void *cookie, uint32_t *cells, uint32_t freq)
+{
+ uint32_t idx = cells[0];
+
+ printf("%s: not handled 0x%08x (freq=0x%08x)\n", __func__, idx, freq);
+
+ return -1;
+}
+
+void
+stfclock_enable_jh7110_pll(void *cookie, uint32_t *cells, int on)
+{
+ uint32_t idx = cells[0];
+
+ printf("%s: not handled 0x%08x\n", __func__, idx);
+}
+
+uint32_t
+stfclock_get_frequency_jh7110_sys(void *cookie, uint32_t *cells)
+{
+ struct stfclock_softc *sc = cookie;
+ uint32_t idx = cells[0];
+ uint32_t parent, freq;
+ uint32_t reg, div, mux;
+
+ switch (idx) {
+ case JH7110_SYSCLK_OSC:
+ return clock_get_frequency(sc->sc_node, "osc");
+ case JH7110_SYSCLK_PLL0_OUT:
+ return clock_get_frequency(sc->sc_node, "pll0_out");
+ case JH7110_SYSCLK_PLL1_OUT:
+ return clock_get_frequency(sc->sc_node, "pll1_out");
+ case JH7110_SYSCLK_PLL2_OUT:
+ return clock_get_frequency(sc->sc_node, "pll2_out");
+ }
+
+ reg = HREAD4(sc, idx * 4);
+ mux = (reg & CLKMUX_MASK) >> CLKMUX_SHIFT;
+ div = (reg & CLKDIV_MASK) >> CLKDIV_SHIFT;
+
+ switch (idx) {
+ case JH7110_SYSCLK_CPU_ROOT:
+ mux = (reg >> 24) & 1;
+ parent = mux ? JH7110_SYSCLK_PLL0_OUT : JH7110_SYSCLK_OSC;
+ return stfclock_get_frequency_jh7110_sys(sc, &parent);
+ case JH7110_SYSCLK_BUS_ROOT:
+ mux = (reg >> 24) & 1;
+ parent = mux ? JH7110_SYSCLK_PLL2_OUT : JH7110_SYSCLK_OSC;
+ return stfclock_get_frequency_jh7110_sys(sc, &parent);
+ }
+
+ switch (idx) {
+ case JH7110_SYSCLK_CPU_CORE:
+ parent = JH7110_SYSCLK_CPU_ROOT;
+ break;
+ case JH7110_SYSCLK_AXI_CFG0:
+ parent = JH7110_SYSCLK_BUS_ROOT;
+ break;
+ case JH7110_SYSCLK_STG_AXIAHB:
+ parent = JH7110_SYSCLK_AXI_CFG0;
+ break;
+ case JH7110_SYSCLK_AHB0:
+ case JH7110_SYSCLK_AHB1:
+ case JH7110_SYSCLK_APB_BUS:
+ parent = JH7110_SYSCLK_STG_AXIAHB;
+ break;
+ case JH7110_SYSCLK_SDIO0_AHB:
+ case JH7110_SYSCLK_SDIO1_AHB:
+ parent = JH7110_SYSCLK_AHB0;
+ break;
+ case JH7110_SYSCLK_SDIO0_SDCARD:
+ case JH7110_SYSCLK_SDIO1_SDCARD:
+ parent = JH7110_SYSCLK_AXI_CFG0;
+ break;
+ case JH7110_SYSCLK_TEMP_APB:
+ parent = JH7110_SYSCLK_APB_BUS;
+ break;
+ case JH7110_SYSCLK_TEMP_CORE:
+ parent = JH7110_SYSCLK_OSC;
+ break;
+ case JH7110_SYSCLK_UART0_CORE:
+ parent = JH7110_SYSCLK_OSC;
+ div = 1;
+ break;
+ default:
+ printf("%s: unknown clock 0x%08x\n", __func__, idx);
+ return 0;
+ }
+
+ if (div == 0) {
+ printf("%s: zero divisor for clock 0x%08x\n", __func__, idx);
+ return 0;
+ }
+
+ freq = stfclock_get_frequency_jh7110_sys(sc, &parent);
+ return freq / div;
+}
+
+int
+stfclock_set_frequency_jh7110_sys(void *cookie, uint32_t *cells, uint32_t freq)
+{
+ uint32_t idx = cells[0];
+
+ printf("%s: not handled 0x%08x (freq=0x%08x)\n", __func__, idx, freq);
+
+ return -1;
+}
+
+void
+stfclock_enable_jh7110_sys(void *cookie, uint32_t *cells, int on)
+{
+ struct stfclock_softc *sc = cookie;
+ uint32_t idx = cells[0];
+
+ switch (idx) {
+ case JH7110_SYSCLK_SDIO0_AHB:
+ case JH7110_SYSCLK_SDIO1_AHB:
+ case JH7110_SYSCLK_SDIO0_SDCARD:
+ case JH7110_SYSCLK_SDIO1_SDCARD:
+ case JH7110_SYSCLK_TEMP_APB:
+ case JH7110_SYSCLK_TEMP_CORE:
+ case JH7110_SYSCLK_UART0_CORE:
+ if (on)
+ HSET4(sc, idx * 4, 1U << 31);
+ else
+ HCLR4(sc, idx * 4, 1U << 31);
+ return;
+ }
+
+ printf("%s: unknown clock 0x%08x\n", __func__, idx);
+}
+
+void
+stfclock_reset_jh7110_sys(void *cookie, uint32_t *cells, int assert)
+{
+ struct stfclock_softc *sc = cookie;
+ uint32_t idx = cells[0];
+ uint32_t bits, offset;
+
+ offset = JH7110_SYSCLK_ASSERT_OFFSET + (idx / 32) * 4;
+ bits = 1U << (idx % 32);
+
+ if (assert)
+ HSET4(sc, offset, bits);
+ else
+ HCLR4(sc, offset, bits);
}