Page MenuHomeFreeBSD

D43037.id135253.diff
No OneTemporary

D43037.id135253.diff

diff --git a/sys/dev/clk/starfive/jh7110_clk.h b/sys/dev/clk/starfive/jh7110_clk.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/starfive/jh7110_clk.h
@@ -0,0 +1,94 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright 2023 Jari Sihvola <jsihv@gmx.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/clk/clk.h>
+
+#ifndef _CLK_JH7110_CLK_H_
+#define _CLK_JH7110_CLK_H_
+
+#define JH7110_CLK_HAS_GATE 0x01
+#define JH7110_CLK_HAS_MUX 0x02
+#define JH7110_CLK_HAS_DIV 0x04
+#define JH7110_CLK_HAS_INV 0x08
+
+#define JH7110_CLK_AON 0x10
+#define JH7110_CLK_ISP 0x20
+#define JH7110_CLK_STG 0x40
+#define JH7110_CLK_SYS 0x80
+#define JH7110_CLK_VOUT 0x100
+
+struct jh7110_clkgen_softc {
+ struct mtx mtx;
+ struct clkdom *clkdom;
+ struct resource *mem_res;
+ int dev_flag;
+};
+
+struct jh7110_clk_def {
+ struct clknode_init_def clkdef;
+ uint32_t offset;
+ uint32_t flags;
+ uint64_t d_max;
+};
+
+#define JH7110_CLK(_idx, _name, _pn, _d_max, _flags) \
+{ \
+ .clkdef.id = _idx, \
+ .clkdef.name = _name, \
+ .clkdef.parent_names = _pn, \
+ .clkdef.parent_cnt = nitems(_pn), \
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
+ .flags = _flags, \
+ .d_max = _d_max, \
+}
+
+#define JH7110_GATE(_idx, _name, _pn) \
+ JH7110_CLK(_idx, _name, _pn, 0, JH7110_CLK_HAS_GATE)
+#define JH7110_MUX(_idx, _name, _pn) \
+ JH7110_CLK(_idx, _name, _pn, 0, JH7110_CLK_HAS_MUX)
+#define JH7110_DIV(_idx, _name, _pn, _d_max) \
+ JH7110_CLK(_idx, _name, _pn, _d_max, JH7110_CLK_HAS_DIV)
+#define JH7110_GATEMUX(_idx, _name, _pn) \
+ JH7110_CLK(_idx, _name, _pn, 0, JH7110_CLK_HAS_GATE | \
+ JH7110_CLK_HAS_MUX)
+#define JH7110_GATEDIV(_idx, _name, _pn, _d_max) \
+ JH7110_CLK(_idx, _name, _pn, _d_max, JH7110_CLK_HAS_GATE | \
+ JH7110_CLK_HAS_DIV)
+#define JH7110_INV(_idx, _name, _pn) \
+ JH7110_CLK(_idx, _name, _pn, 0, JH7110_CLK_HAS_INV)
+
+int jh7110_clk_register(struct clkdom *clkdom,
+ const struct jh7110_clk_def *clkdef);
+
+int jh7110_ofw_map(struct clkdom *clkdom, uint32_t ncells,
+ phandle_t *cells, struct clknode **clk);
+
+int jh7110_reset_is_asserted(device_t dev, intptr_t id, bool *reset);
+
+int jh7110_reset_assert(device_t dev, intptr_t id, bool assert);
+
+#endif /* _CLK_JH7110_CLK_H_ */
diff --git a/sys/dev/clk/starfive/jh7110_clk.c b/sys/dev/clk/starfive/jh7110_clk.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/starfive/jh7110_clk.c
@@ -0,0 +1,371 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ * Copyright (c) 2022 Mitchell Horne <mhorne@FreeBSD.org>
+ * Copyright (c) 2023 Jari Sihvola <jsihv@gmx.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/fbio.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/resource.h>
+
+#include <dev/clk/clk.h>
+#include <dev/hwreset/hwreset.h>
+
+#include <dt-bindings/clock/starfive,jh7110-crg.h>
+
+#include <dev/clk/starfive/jh7110_clk.h>
+
+#include "clkdev_if.h"
+#include "hwreset_if.h"
+
+#define JH7110_DIV_MASK 0xffffff
+#define JH7110_MUX_SHIFT 24
+#define JH7110_MUX_MASK 0x3f000000
+#define JH7110_ENABLE_SHIFT 31
+#define REG_SIZE 4
+
+/* offsets for reset registers */
+#define AONCRG_RESET_SELECTOR 0x38
+#define STGCRG_RESET_SELECTOR 0x74
+#define AONCRG_RESET_STATUS 0x3c
+#define STGCRG_RESET_STATUS 0x78
+#define SYSCRG_RESET_SELECTOR0 0x2f8
+#define SYSCRG_RESET_SELECTOR1 0x2fc
+#define SYSCRG_RESET_SELECTOR2 0x300
+#define SYSCRG_RESET_SELECTOR3 0x304
+#define SYSCRG_RESET_STATUS0 0x308
+#define SYSCRG_RESET_STATUS1 0x30c
+#define SYSCRG_RESET_STATUS2 0x310
+#define SYSCRG_RESET_STATUS3 0x314
+
+enum jh7110_reset_crg {
+ SYS0,
+ SYS1,
+ SYS2,
+ SYS3,
+ STG,
+ AON,
+};
+
+struct jh7110_clk_sc {
+ uint32_t offset;
+ uint32_t flags;
+ uint64_t d_max;
+ int id;
+};
+
+struct jh7110_rstdata {
+ int crg;
+ uint64_t status_offset;
+ uint64_t selector_offset;
+};
+
+#define DIV_ROUND_CLOSEST(n, d) (((n) + (d) / 2) / (d))
+
+#define READ4(_sc, _off) \
+ bus_read_4(_sc->mem_res, _off)
+#define WRITE4(_sc, _off, _val) \
+ bus_write_4(_sc->mem_res, _off, _val)
+
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+/* Reset functions */
+
+static void
+jh7110_reset_io_assign(device_t dev, intptr_t id, struct jh7110_rstdata *iodata)
+{
+ struct jh7110_clkgen_softc *sc;
+ uint32_t crg;
+
+ sc = device_get_softc(dev);
+
+ if (sc->dev_flag == JH7110_CLK_AON) {
+ iodata->crg = AON;
+ iodata->status_offset = AONCRG_RESET_STATUS;
+ iodata->selector_offset = AONCRG_RESET_SELECTOR;
+
+ return;
+ }
+
+ crg = id / 32;
+
+ if (crg == SYS0) {
+ iodata->crg = SYS0;
+ iodata->status_offset = SYSCRG_RESET_STATUS0;
+ iodata->selector_offset = SYSCRG_RESET_SELECTOR0;
+ } else if (crg == SYS1) {
+ iodata->crg = SYS1;
+ iodata->status_offset = SYSCRG_RESET_STATUS1;
+ iodata->selector_offset = SYSCRG_RESET_SELECTOR1;
+ } else if (crg == SYS2) {
+ iodata->crg = SYS2;
+ iodata->status_offset = SYSCRG_RESET_STATUS2;
+ iodata->selector_offset = SYSCRG_RESET_SELECTOR2;
+ } else if (crg == SYS3) {
+ iodata->crg = SYS3;
+ iodata->status_offset = SYSCRG_RESET_STATUS3;
+ iodata->selector_offset = SYSCRG_RESET_SELECTOR3;
+ } else
+ panic("Reset with id %lu, crg %d has no group\n", id, crg);
+
+ return;
+}
+
+int
+jh7110_reset_assert(device_t dev, intptr_t id, bool assert)
+{
+ struct jh7110_clkgen_softc *sc;
+ struct jh7110_rstdata iodata;
+ uint32_t regvalue, bitmask = 1UL << id % 32;
+
+ sc = device_get_softc(dev);
+
+ jh7110_reset_io_assign(dev, id, &iodata);
+
+ mtx_lock(&sc->mtx);
+
+ regvalue = READ4(sc, iodata.selector_offset);
+
+ if (assert)
+ regvalue |= bitmask;
+ else
+ regvalue &= ~bitmask;
+ WRITE4(sc, iodata.selector_offset, regvalue);
+
+ mtx_unlock(&sc->mtx);
+
+ return (0);
+}
+
+int
+jh7110_reset_is_asserted(device_t dev, intptr_t id, bool *reset)
+{
+ struct jh7110_clkgen_softc *sc;
+ struct jh7110_rstdata iodata;
+ uint32_t regvalue;
+ uint32_t bitmask;
+
+ sc = device_get_softc(dev);
+
+ jh7110_reset_io_assign(dev, id, &iodata);
+
+ mtx_lock(&sc->mtx);
+
+ regvalue = READ4(sc, iodata.status_offset);
+ bitmask = 1UL << id % 32;
+
+ mtx_unlock(&sc->mtx);
+
+ *reset = (regvalue & bitmask) == 0;
+
+ return (0);
+}
+
+/* Clock functions */
+
+static int
+jh7110_clk_init(struct clknode *clk, device_t dev)
+{
+ struct jh7110_clkgen_softc *sc;
+ struct jh7110_clk_sc *sc_clk;
+ uint32_t reg;
+ int idx = 0;
+
+ sc = device_get_softc(clknode_get_device(clk));
+ sc_clk = clknode_get_softc(clk);
+
+ if (sc_clk->flags & JH7110_CLK_HAS_MUX) {
+ DEVICE_LOCK(clk);
+ reg = READ4(sc, sc_clk->offset);
+ DEVICE_UNLOCK(clk);
+ idx = (reg & JH7110_MUX_MASK) >> JH7110_MUX_SHIFT;
+ }
+
+ clknode_init_parent_idx(clk, idx);
+
+ return (0);
+}
+
+static int
+jh7110_clk_set_gate(struct clknode *clk, bool enable)
+{
+ struct jh7110_clkgen_softc *sc;
+ struct jh7110_clk_sc *sc_clk;
+ uint32_t reg;
+
+ sc = device_get_softc(clknode_get_device(clk));
+ sc_clk = clknode_get_softc(clk);
+
+ if ((sc_clk->flags & JH7110_CLK_HAS_GATE) == 0)
+ return (0);
+
+ DEVICE_LOCK(clk);
+
+ reg = READ4(sc, sc_clk->offset);
+ if (enable)
+ reg |= (1 << JH7110_ENABLE_SHIFT);
+ else
+ reg &= ~(1 << JH7110_ENABLE_SHIFT);
+ WRITE4(sc, sc_clk->offset, reg);
+
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static int
+jh7110_clk_set_mux(struct clknode *clk, int idx)
+{
+ struct jh7110_clkgen_softc *sc;
+ struct jh7110_clk_sc *sc_clk;
+ uint32_t reg;
+
+ sc = device_get_softc(clknode_get_device(clk));
+ sc_clk = clknode_get_softc(clk);
+
+ if ((sc_clk->flags & JH7110_CLK_HAS_MUX) == 0)
+ return (ENXIO);
+
+ /* Checking index size */
+ if ((idx & (JH7110_MUX_MASK >> JH7110_MUX_SHIFT)) != idx)
+ return (EINVAL);
+
+ DEVICE_LOCK(clk);
+
+ reg = READ4(sc, sc_clk->offset) & ~JH7110_MUX_MASK;
+ reg |= idx << JH7110_MUX_SHIFT;
+ WRITE4(sc, sc_clk->offset, reg);
+
+ DEVICE_UNLOCK(clk);
+
+ return (0);
+}
+
+static int
+jh7110_clk_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct jh7110_clkgen_softc *sc;
+ struct jh7110_clk_sc *sc_clk;
+ uint32_t divisor;
+
+ sc = device_get_softc(clknode_get_device(clk));
+ sc_clk = clknode_get_softc(clk);
+
+ /* Returning error here causes panic */
+ if ((sc_clk->flags & JH7110_CLK_HAS_DIV) == 0)
+ return (0);
+
+ DEVICE_LOCK(clk);
+
+ divisor = READ4(sc, sc_clk->offset) & JH7110_DIV_MASK;
+
+ DEVICE_UNLOCK(clk);
+
+ if (divisor)
+ *freq = *freq / divisor;
+ else
+ *freq = 0;
+
+ return (0);
+}
+
+static int
+jh7110_clk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+ int flags, int *done)
+{
+ struct jh7110_clkgen_softc *sc;
+ struct jh7110_clk_sc *sc_clk;
+ uint32_t divisor;
+
+ sc = device_get_softc(clknode_get_device(clk));
+ sc_clk = clknode_get_softc(clk);
+
+ if ((sc_clk->flags & JH7110_CLK_HAS_DIV) == 0)
+ return (0);
+
+ divisor = MIN(MAX(DIV_ROUND_CLOSEST(fin, *fout), 1UL), sc_clk->d_max);
+
+ if (flags & CLK_SET_DRYRUN)
+ goto done;
+
+ DEVICE_LOCK(clk);
+
+ divisor |= READ4(sc, sc_clk->offset) & ~JH7110_DIV_MASK;
+ WRITE4(sc, sc_clk->offset, divisor);
+
+ DEVICE_UNLOCK(clk);
+
+done:
+ *fout = divisor;
+ *done = 1;
+
+ return (0);
+}
+
+static clknode_method_t jh7110_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, jh7110_clk_init),
+ CLKNODEMETHOD(clknode_set_gate, jh7110_clk_set_gate),
+ CLKNODEMETHOD(clknode_set_mux, jh7110_clk_set_mux),
+ CLKNODEMETHOD(clknode_recalc_freq, jh7110_clk_recalc_freq),
+ CLKNODEMETHOD(clknode_set_freq, jh7110_clk_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(jh7110_clknode, jh7110_clknode_class, jh7110_clknode_methods,
+ sizeof(struct jh7110_clk_sc), clknode_class);
+
+int
+jh7110_clk_register(struct clkdom *clkdom, const struct jh7110_clk_def *clkdef)
+{
+ struct clknode *clk;
+ struct jh7110_clk_sc *sc;
+
+ clk = clknode_create(clkdom, &jh7110_clknode_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (-1);
+
+ sc = clknode_get_softc(clk);
+
+ sc->offset = clkdef->clkdef.id * REG_SIZE;
+
+ sc->flags = clkdef->flags;
+ sc->id = clkdef->clkdef.id;
+ sc->d_max = clkdef->d_max;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/dev/clk/starfive/jh7110_clk_aon.c b/sys/dev/clk/starfive/jh7110_clk_aon.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/starfive/jh7110_clk_aon.c
@@ -0,0 +1,198 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ * Copyright (c) 2024 Jari Sihvola <jsihv@gmx.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Clocks for JH7110 AON group. PLL driver must be attached before this. */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/fbio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <dev/clk/clk.h>
+#include <dev/clk/clk_div.h>
+#include <dev/clk/clk_gate.h>
+#include <dev/clk/clk_link.h>
+#include <dev/clk/clk_mux.h>
+
+#include <dev/fdt/simplebus.h>
+#include <dev/hwreset/hwreset.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/starfive,jh7110-crg.h>
+
+#include <dev/clk/starfive/jh7110_clk.h>
+
+#include "clkdev_if.h"
+#include "hwreset_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ { "starfive,jh7110-aoncrg", 1 },
+ { NULL, 0 }
+};
+
+static struct resource_spec res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE },
+ RESOURCE_SPEC_END
+};
+
+/* parents */
+static const char *gmac0_axi_p[] = { "stg_axiahb" };
+static const char *gmac0_ahb_p[] = { "stg_axiahb" };
+static const char *gmac0_tx_inv_p[] = { "gmac0_tx" };
+static const char *gmac0_tx_p[] = { "gmac0_gtxclk", "gmac0_rmii_rtx" };
+static const char *gmac0_rmii_rtx_p[] = { "gmac0_rmii_refin" };
+
+/* AON clocks */
+static const struct jh7110_clk_def aon_clks[] = {
+ JH7110_GATE(JH7110_AONCLK_GMAC0_AXI, "gmac0_axi", gmac0_axi_p),
+ JH7110_GATE(JH7110_AONCLK_GMAC0_AHB, "gmac0_ahb", gmac0_ahb_p),
+ JH7110_GATEMUX(JH7110_AONCLK_GMAC0_TX, "gmac0_tx", gmac0_tx_p),
+ JH7110_INV(JH7110_AONCLK_GMAC0_TX_INV, "gmac0_tx_inv", gmac0_tx_inv_p),
+ JH7110_DIV(JH7110_AONCLK_GMAC0_RMII_RTX, "gmac0_rmii_rtx",
+ gmac0_rmii_rtx_p, 30),
+};
+
+static int
+jh7110_clk_aon_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "StarFive JH7110 AON clock generator");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jh7110_clk_aon_attach(device_t dev)
+{
+ struct jh7110_clkgen_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->dev_flag = JH7110_CLK_AON;
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ err = bus_alloc_resources(dev, res_spec, &sc->mem_res);
+ if (err != 0) {
+ device_printf(dev,
+ "Couldn't allocate resources, error %d\n", err);
+ return (ENXIO);
+ }
+
+ sc->clkdom = clkdom_create(dev);
+ if (sc->clkdom == NULL) {
+ device_printf(dev, "Couldn't create clkdom, error %d\n", err);
+ return (ENXIO);
+ }
+
+ for (int i = 0; i < nitems(aon_clks); i++) {
+ err = jh7110_clk_register(sc->clkdom, &aon_clks[i]);
+ if (err != 0) {
+ device_printf(
+ dev, "Couldn't register clk %s, error %d\n",
+ aon_clks[i].clkdef.name, err);
+ return (ENXIO);
+ }
+ }
+
+ if (clkdom_finit(sc->clkdom) != 0)
+ panic("Cannot finalize clkdom initialization\n");
+
+ if (bootverbose)
+ clkdom_dump(sc->clkdom);
+
+ hwreset_register_ofw_provider(dev);
+
+ return (0);
+}
+
+static void
+jh7110_clk_aon_device_lock(device_t dev)
+{
+ struct jh7110_clkgen_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+static void
+jh7110_clk_aon_device_unlock(device_t dev)
+{
+ struct jh7110_clkgen_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+static int
+jh7110_clk_aon_detach(device_t dev)
+{
+ /* Detach not supported */
+ return (EACCES);
+}
+
+static device_method_t jh7110_clk_aon_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jh7110_clk_aon_probe),
+ DEVMETHOD(device_attach, jh7110_clk_aon_attach),
+ DEVMETHOD(device_detach, jh7110_clk_aon_detach),
+
+ /* clkdev interface */
+ DEVMETHOD(clkdev_device_lock, jh7110_clk_aon_device_lock),
+ DEVMETHOD(clkdev_device_unlock, jh7110_clk_aon_device_unlock),
+
+ /* Reset interface */
+ DEVMETHOD(hwreset_assert, jh7110_reset_assert),
+ DEVMETHOD(hwreset_is_asserted, jh7110_reset_is_asserted),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(jh7110_aon, jh7110_aon_driver, jh7110_clk_aon_methods,
+ sizeof(struct jh7110_clkgen_softc));
+EARLY_DRIVER_MODULE(jh7110_aon, simplebus, jh7110_aon_driver, 0, 0,
+ BUS_PASS_BUS + BUS_PASS_ORDER_LATE);
+MODULE_VERSION(jh7110_aon, 1);
diff --git a/sys/dev/clk/starfive/jh7110_clk_pll.h b/sys/dev/clk/starfive/jh7110_clk_pll.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/starfive/jh7110_clk_pll.h
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * StarFive JH7110 PLL Clock Generator Driver
+ *
+ * Copyright (C) 2022 Xingyu Wu <xingyu.wu@starfivetech.com>
+ */
+
+#define PLL0_DACPD_SHIFT 24
+#define PLL0_DACPD_MASK 0x1000000
+#define PLL0_DSMPD_SHIFT 25
+#define PLL0_DSMPD_MASK 0x2000000
+#define PLL0_FBDIV_SHIFT 0
+#define PLL0_FBDIV_MASK 0xFFF
+#define PLL0_FRAC_SHIFT 0
+#define PLL0_FRAC_MASK 0xFFFFFF
+#define PLL0_POSTDIV1_SHIFT 28
+#define PLL0_POSTDIV1_MASK 0x30000000
+#define PLL0_PREDIV_SHIFT 0
+#define PLL0_PREDIV_MASK 0x3F
+
+#define PLL1_DACPD_SHIFT 15
+#define PLL1_DACPD_MASK 0x8000
+#define PLL1_DSMPD_SHIFT 16
+#define PLL1_DSMPD_MASK 0x10000
+#define PLL1_FBDIV_SHIFT 17
+#define PLL1_FBDIV_MASK 0x1FFE0000
+#define PLL1_FRAC_SHIFT 0
+#define PLL1_FRAC_MASK 0xFFFFFF
+#define PLL1_POSTDIV1_SHIFT 28
+#define PLL1_POSTDIV1_MASK 0x30000000
+#define PLL1_PREDIV_SHIFT 0
+#define PLL1_PREDIV_MASK 0x3F
+
+#define PLL2_DACPD_SHIFT 15
+#define PLL2_DACPD_MASK 0x8000
+#define PLL2_DSMPD_SHIFT 16
+#define PLL2_DSMPD_MASK 0x10000
+#define PLL2_FBDIV_SHIFT 17
+#define PLL2_FBDIV_MASK 0x1FFE0000
+#define PLL2_FRAC_SHIFT 0
+#define PLL2_FRAC_MASK 0xFFFFFF
+#define PLL2_POSTDIV1_SHIFT 28
+#define PLL2_POSTDIV1_MASK 0x30000000
+#define PLL2_PREDIV_SHIFT 0
+#define PLL2_PREDIV_MASK 0x3F
+
+#define FRAC_PATR_SIZE 1000
+
+struct jh7110_pll_syscon_value {
+ uint64_t freq;
+ uint32_t prediv;
+ uint32_t fbdiv;
+ uint32_t postdiv1;
+ uint32_t dacpd;
+ uint32_t dsmpd;
+ uint32_t frac;
+};
+
+enum starfive_pll0_freq_value {
+ PLL0_FREQ_375_VALUE = 375000000,
+ PLL0_FREQ_500_VALUE = 500000000,
+ PLL0_FREQ_625_VALUE = 625000000,
+ PLL0_FREQ_750_VALUE = 750000000,
+ PLL0_FREQ_875_VALUE = 875000000,
+ PLL0_FREQ_1000_VALUE = 1000000000,
+ PLL0_FREQ_1250_VALUE = 1250000000,
+ PLL0_FREQ_1375_VALUE = 1375000000,
+ PLL0_FREQ_1500_VALUE = 1500000000
+};
+
+enum starfive_pll0_freq {
+ PLL0_FREQ_375 = 0,
+ PLL0_FREQ_500,
+ PLL0_FREQ_625,
+ PLL0_FREQ_750,
+ PLL0_FREQ_875,
+ PLL0_FREQ_1000,
+ PLL0_FREQ_1250,
+ PLL0_FREQ_1375,
+ PLL0_FREQ_1500,
+ PLL0_FREQ_MAX = PLL0_FREQ_1500
+};
+
+enum starfive_pll1_freq_value {
+ PLL1_FREQ_1066_VALUE = 1066000000,
+};
+
+enum starfive_pll1_freq {
+ PLL1_FREQ_1066 = 0,
+};
+
+enum starfive_pll2_freq_value {
+ PLL2_FREQ_1188_VALUE = 1188000000,
+ PLL2_FREQ_12288_VALUE = 1228800000,
+};
+
+enum starfive_pll2_freq {
+ PLL2_FREQ_1188 = 0,
+ PLL2_FREQ_12288,
+};
+
+static const struct jh7110_pll_syscon_value
+ jh7110_pll0_syscon_freq[] = {
+ [PLL0_FREQ_375] = {
+ .freq = PLL0_FREQ_375_VALUE,
+ .prediv = 8,
+ .fbdiv = 125,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+ [PLL0_FREQ_500] = {
+ .freq = PLL0_FREQ_500_VALUE,
+ .prediv = 6,
+ .fbdiv = 125,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+ [PLL0_FREQ_625] = {
+ .freq = PLL0_FREQ_625_VALUE,
+ .prediv = 24,
+ .fbdiv = 625,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+ [PLL0_FREQ_750] = {
+ .freq = PLL0_FREQ_750_VALUE,
+ .prediv = 4,
+ .fbdiv = 125,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+ [PLL0_FREQ_875] = {
+ .freq = PLL0_FREQ_875_VALUE,
+ .prediv = 24,
+ .fbdiv = 875,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+ [PLL0_FREQ_1000] = {
+ .freq = PLL0_FREQ_1000_VALUE,
+ .prediv = 3,
+ .fbdiv = 125,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+ [PLL0_FREQ_1250] = {
+ .freq = PLL0_FREQ_1250_VALUE,
+ .prediv = 12,
+ .fbdiv = 625,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+ [PLL0_FREQ_1375] = {
+ .freq = PLL0_FREQ_1375_VALUE,
+ .prediv = 24,
+ .fbdiv = 1375,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+ [PLL0_FREQ_1500] = {
+ .freq = PLL0_FREQ_1500_VALUE,
+ .prediv = 2,
+ .fbdiv = 125,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+};
+
+static const struct jh7110_pll_syscon_value
+ jh7110_pll1_syscon_freq[] = {
+ [PLL1_FREQ_1066] = {
+ .freq = PLL1_FREQ_1066_VALUE,
+ .prediv = 12,
+ .fbdiv = 533,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+};
+
+static const struct jh7110_pll_syscon_value
+ jh7110_pll2_syscon_freq[] = {
+ [PLL2_FREQ_1188] = {
+ .freq = PLL2_FREQ_1188_VALUE,
+ .prediv = 2,
+ .fbdiv = 99,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+ [PLL2_FREQ_12288] = {
+ .freq = PLL2_FREQ_12288_VALUE,
+ .prediv = 5,
+ .fbdiv = 256,
+ .postdiv1 = 1,
+ .dacpd = 1,
+ .dsmpd = 1,
+ },
+};
diff --git a/sys/dev/clk/starfive/jh7110_clk_pll.c b/sys/dev/clk/starfive/jh7110_clk_pll.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/starfive/jh7110_clk_pll.c
@@ -0,0 +1,459 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2024 Jari Sihvola <jsihv@gmx.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/fbio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/starfive,jh7110-crg.h>
+
+#include <dev/clk/starfive/jh7110_clk_pll.h>
+#include <dev/clk/starfive/jh7110_clk.h>
+#include <dev/clk/clk.h>
+
+#include "clkdev_if.h"
+
+#define PLL_OFFSET_0_ 0x18
+#define PLL_OFFSET_1_ 0x1c
+#define PLL_OFFSET_2_ 0x20
+#define PLL_OFFSET_3_ 0x24
+#define PLL_OFFSET_4_ 0x28
+#define PLL_OFFSET_5_ 0x2c
+#define PLL_OFFSET_6_ 0x30
+#define PLL_OFFSET_7_ 0x34
+
+#define DEVICE_LOCK(_clk) \
+ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define DEVICE_UNLOCK(_clk) \
+ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+#define SYSCON_READ_4(reg) \
+ bus_read_4(sc->syscon_mem_res, (reg))
+#define SYSCON_MODIFY_4(reg, cbits, sbits) \
+ jh7110_syscon_modify_4(sc->syscon_mem_res, (reg), (cbits), (sbits))
+
+#define PLL_MASK_FILL(id) do { \
+ sc->pll_data[id].dacpd_mask = PLL## id ##_DACPD_MASK; \
+ sc->pll_data[id].dsmpd_mask = PLL## id ##_DSMPD_MASK; \
+ sc->pll_data[id].fbdiv_mask = PLL## id ##_FBDIV_MASK; \
+ sc->pll_data[id].frac_mask = PLL## id ##_FRAC_MASK; \
+ sc->pll_data[id].prediv_mask = PLL## id ##_PREDIV_MASK; \
+ sc->pll_data[id].postdiv1_mask = PLL## id ##_POSTDIV1_MASK; \
+} while (0)
+
+#define PLL_SHIFT_FILL(id) do { \
+ sc->pll_data[id].dacpd_shift = PLL## id ##_DACPD_SHIFT; \
+ sc->pll_data[id].dsmpd_shift = PLL## id ##_DSMPD_SHIFT; \
+ sc->pll_data[id].fbdiv_shift = PLL## id ##_FBDIV_SHIFT; \
+ sc->pll_data[id].frac_shift = PLL## id ##_FRAC_SHIFT; \
+ sc->pll_data[id].prediv_shift = PLL## id ##_PREDIV_SHIFT; \
+ sc->pll_data[id].postdiv1_shift = PLL## id ##_POSTDIV1_SHIFT; \
+} while (0)
+
+#define PLL_OFFSET_FILL(id, val1, val2, val3, val4, val5, val6) do { \
+ sc->pll_data[id].dacpd_offset = PLL_OFFSET_## val1 ##_ ; \
+ sc->pll_data[id].dsmpd_offset = PLL_OFFSET_## val2 ##_ ; \
+ sc->pll_data[id].fbdiv_offset = PLL_OFFSET_## val3 ##_ ; \
+ sc->pll_data[id].frac_offset = PLL_OFFSET_## val4 ##_ ; \
+ sc->pll_data[id].prediv_offset = PLL_OFFSET_## val5 ##_ ; \
+ sc->pll_data[id].postdiv1_offset = PLL_OFFSET_## val6 ##_ ; \
+} while (0)
+
+static struct ofw_compat_data compat_data[] = {
+ { "starfive,jh7110-sys-syscon", 1 },
+ { NULL, 0 }
+};
+
+static struct resource_spec res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE },
+ RESOURCE_SPEC_END
+};
+
+struct pll_clk_data {
+ uint32_t dacpd_offset;
+ uint32_t dsmpd_offset;
+ uint32_t fbdiv_offset;
+ uint32_t frac_offset;
+ uint32_t prediv_offset;
+ uint32_t postdiv1_offset;
+
+ uint32_t dacpd_mask;
+ uint32_t dsmpd_mask;
+ uint32_t fbdiv_mask;
+ uint32_t frac_mask;
+ uint32_t prediv_mask;
+ uint32_t postdiv1_mask;
+
+ uint32_t dacpd_shift;
+ uint32_t dsmpd_shift;
+ uint32_t fbdiv_shift;
+ uint32_t frac_shift;
+ uint32_t prediv_shift;
+ uint32_t postdiv1_shift;
+
+ const struct
+ jh7110_pll_syscon_value *syscon_arr;
+ int syscon_nitems;
+};
+
+struct jh7110_clk_pll_softc {
+ struct mtx mtx;
+ struct clkdom *clkdom;
+ struct resource *syscon_mem_res;
+};
+
+struct jh7110_clk_pll_sc {
+ int id;
+ struct pll_clk_data pll_data[JH7110_PLLCLK_END];
+};
+
+static const char *pll_parents[] = { "osc" };
+
+static struct jh7110_clk_def pll_out_clks[] = {
+ {
+ .clkdef.id = JH7110_PLLCLK_PLL0_OUT,
+ .clkdef.name = "pll0_out",
+ .clkdef.parent_names = pll_parents,
+ .clkdef.parent_cnt = nitems(pll_parents),
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS,
+ },
+ {
+ .clkdef.id = JH7110_PLLCLK_PLL1_OUT,
+ .clkdef.name = "pll1_out",
+ .clkdef.parent_names = pll_parents,
+ .clkdef.parent_cnt = nitems(pll_parents),
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS,
+ },
+ {
+ .clkdef.id = JH7110_PLLCLK_PLL2_OUT,
+ .clkdef.name = "pll2_out",
+ .clkdef.parent_names = pll_parents,
+ .clkdef.parent_cnt = nitems(pll_parents),
+ .clkdef.flags = CLK_NODE_STATIC_STRINGS,
+ },
+};
+
+static int jh7110_clk_pll_register(struct clkdom *clkdom,
+ struct jh7110_clk_def *clkdef);
+
+static int
+jh7110_syscon_modify_4(struct resource *res, bus_size_t offset,
+ uint32_t clear_bits, uint32_t set_bits)
+{
+ uint32_t val;
+ uint32_t new_val;
+
+ val = bus_read_4(res, offset);
+
+ new_val = (val & ~clear_bits) | set_bits;
+
+ if (new_val != val)
+ bus_write_4(res, offset, new_val);
+
+ return (0);
+}
+
+static int
+jh7110_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct jh7110_clk_pll_softc *sc;
+ struct jh7110_clk_pll_sc *clk_sc;
+ struct pll_clk_data *data;
+ uint32_t dacpd, dsmpd, fbdiv, prediv, postdiv1;
+ uint64_t frac, fcal = 0;
+
+ sc = device_get_softc(clknode_get_device(clk));
+ clk_sc = clknode_get_softc(clk);
+
+ if (clk_sc->id == JH7110_PLLCLK_PLL0_OUT)
+ data = &clk_sc->pll_data[0];
+ else if (clk_sc->id == JH7110_PLLCLK_PLL1_OUT)
+ data = &clk_sc->pll_data[1];
+ else
+ data = &clk_sc->pll_data[2];
+
+ DEVICE_LOCK(clk);
+
+ dacpd = (SYSCON_READ_4(data->dacpd_offset) & data->dacpd_mask) >>
+ data->dacpd_shift;
+ dsmpd = (SYSCON_READ_4(data->dsmpd_offset) & data->dsmpd_mask) >>
+ data->dsmpd_shift;
+ fbdiv = (SYSCON_READ_4(data->fbdiv_offset) & data->fbdiv_mask) >>
+ data->fbdiv_shift;
+ prediv = (SYSCON_READ_4(data->prediv_offset) & data->prediv_mask) >>
+ data->prediv_shift;
+ postdiv1 = (SYSCON_READ_4(data->postdiv1_offset) &
+ data->postdiv1_mask) >> data->postdiv1_shift;
+ frac = (SYSCON_READ_4(data->frac_offset) & data->frac_mask) >>
+ data->frac_shift;
+
+ DEVICE_UNLOCK(clk);
+
+ /* dacpd and dsmpd both being 0 entails Fraction Multiple Mode */
+ if (dacpd == 0 && dsmpd == 0)
+ fcal = frac * FRAC_PATR_SIZE / (1 << 24);
+
+ *freq = *freq / FRAC_PATR_SIZE * (fbdiv * FRAC_PATR_SIZE +
+ fcal) / prediv / (1 << postdiv1);
+
+ return (0);
+}
+
+static int
+jh7110_clk_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+ int flags, int *done)
+{
+ struct jh7110_clk_pll_softc *sc;
+ struct jh7110_clk_pll_sc *clk_sc;
+ struct pll_clk_data *data;
+ const struct jh7110_pll_syscon_value *syscon_val = NULL;
+
+ sc = device_get_softc(clknode_get_device(clk));
+ clk_sc = clknode_get_softc(clk);
+
+ if (clk_sc->id == JH7110_PLLCLK_PLL0_OUT)
+ data = &clk_sc->pll_data[0];
+ else if (clk_sc->id == JH7110_PLLCLK_PLL1_OUT)
+ data = &clk_sc->pll_data[1];
+ else
+ data = &clk_sc->pll_data[2];
+
+ for (int i = 0; i != data->syscon_nitems; i++) {
+ if (*fout == data->syscon_arr[i].freq) {
+ syscon_val = &data->syscon_arr[i];
+ }
+ }
+
+ if (syscon_val == NULL) {
+ printf("%s: tried to set an unknown frequency %ju for %s\n",
+ __func__, *fout, clknode_get_name(clk));
+ return (EINVAL);
+ }
+
+ if (flags & CLK_SET_DRYRUN) {
+ *done = 1;
+ return (0);
+ }
+
+ DEVICE_LOCK(clk);
+
+ SYSCON_MODIFY_4(data->dacpd_offset, data->dacpd_mask,
+ syscon_val->dacpd << data->dacpd_shift & data->dacpd_mask);
+ SYSCON_MODIFY_4(data->dsmpd_offset, data->dsmpd_mask,
+ syscon_val->dsmpd << data->dsmpd_shift & data->dsmpd_mask);
+ SYSCON_MODIFY_4(data->prediv_offset, data->prediv_mask,
+ syscon_val->prediv << data->prediv_shift & data->prediv_mask);
+ SYSCON_MODIFY_4(data->fbdiv_offset, data->fbdiv_mask,
+ syscon_val->fbdiv << data->fbdiv_shift & data->fbdiv_mask);
+ SYSCON_MODIFY_4(data->postdiv1_offset,
+ data->postdiv1_mask, (syscon_val->postdiv1 >> 1)
+ << data->postdiv1_shift & data->postdiv1_mask);
+
+ if (!syscon_val->dacpd && !syscon_val->dsmpd) {
+ SYSCON_MODIFY_4(data->frac_offset, data->frac_mask,
+ syscon_val->frac << data->frac_shift & data->frac_mask);
+ }
+
+ DEVICE_UNLOCK(clk);
+
+ *done = 1;
+
+ return (0);
+}
+
+static int
+jh7110_clk_pll_init(struct clknode *clk, device_t dev)
+{
+ clknode_init_parent_idx(clk, 0);
+
+ return (0);
+}
+
+static int
+jh7110_clk_pll_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "StarFive JH7110 pll clock generator");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jh7110_clk_pll_attach(device_t dev)
+{
+ struct jh7110_clk_pll_softc *sc;
+ int i, error;
+
+ sc = device_get_softc(dev);
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ error = bus_alloc_resources(dev, res_spec, &sc->syscon_mem_res);
+ if (error) {
+ device_printf(dev, "Couldn't allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->clkdom = clkdom_create(dev);
+ if (sc->clkdom == NULL) {
+ device_printf(dev, "Couldn't create clkdom\n");
+ return (ENXIO);
+ }
+
+ for (i = 0; i < nitems(pll_out_clks); i++) {
+ error = jh7110_clk_pll_register(sc->clkdom, &pll_out_clks[i]);
+ if (error != 0)
+ device_printf(dev, "Couldn't register clock %s: %d\n",
+ pll_out_clks[i].clkdef.name, error);
+ }
+
+ error = clkdom_finit(sc->clkdom);
+ if (error) {
+ device_printf(dev, "Clkdom_finit() returns error %d\n", error);
+ }
+
+ if (bootverbose)
+ clkdom_dump(sc->clkdom);
+
+ return (0);
+}
+
+static void
+jh7110_clk_pll_device_lock(device_t dev)
+{
+ struct jh7110_clk_pll_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+static void
+jh7110_clk_pll_device_unlock(device_t dev)
+{
+ struct jh7110_clk_pll_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+static clknode_method_t jh7110_pllnode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, jh7110_clk_pll_init),
+ CLKNODEMETHOD(clknode_recalc_freq, jh7110_clk_pll_recalc_freq),
+ CLKNODEMETHOD(clknode_set_freq, jh7110_clk_pll_set_freq),
+
+ CLKNODEMETHOD_END
+};
+
+static device_method_t jh7110_clk_pll_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jh7110_clk_pll_probe),
+ DEVMETHOD(device_attach, jh7110_clk_pll_attach),
+
+ /* clkdev interface */
+ DEVMETHOD(clkdev_device_lock, jh7110_clk_pll_device_lock),
+ DEVMETHOD(clkdev_device_unlock, jh7110_clk_pll_device_unlock),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(jh7110_pllnode, jh7110_pllnode_class, jh7110_pllnode_methods,
+ sizeof(struct jh7110_clk_pll_sc), clknode_class);
+DEFINE_CLASS_0(jh7110_clk_pll, jh7110_clk_pll_driver, jh7110_clk_pll_methods,
+ sizeof(struct jh7110_clk_pll_softc));
+EARLY_DRIVER_MODULE(jh7110_clk_pll, simplebus, jh7110_clk_pll_driver, 0, 0,
+ BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
+MODULE_VERSION(jh7110_clk_pll, 1);
+
+int
+jh7110_clk_pll_register(struct clkdom *clkdom, struct jh7110_clk_def *clkdef)
+{
+ struct clknode *clk = NULL;
+ struct jh7110_clk_pll_sc *sc;
+
+ clk = clknode_create(clkdom, &jh7110_pllnode_class, &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+ sc->id = clkdef->clkdef.id;
+
+ switch (sc->id) {
+ case JH7110_PLLCLK_PLL0_OUT:
+ sc->pll_data[sc->id].syscon_arr =
+ jh7110_pll0_syscon_freq;
+ sc->pll_data[sc->id].syscon_nitems =
+ nitems(jh7110_pll0_syscon_freq);
+ PLL_MASK_FILL(0);
+ PLL_SHIFT_FILL(0);
+ PLL_OFFSET_FILL(sc->id, 0, 0, 1, 2, 3, 2);
+ break;
+ case JH7110_PLLCLK_PLL1_OUT:
+ sc->pll_data[sc->id].syscon_arr =
+ jh7110_pll1_syscon_freq;
+ sc->pll_data[sc->id].syscon_nitems =
+ nitems(jh7110_pll1_syscon_freq);
+ PLL_MASK_FILL(1);
+ PLL_SHIFT_FILL(1);
+ PLL_OFFSET_FILL(sc->id, 3, 3, 3, 4, 5, 4);
+ break;
+ case JH7110_PLLCLK_PLL2_OUT:
+ sc->pll_data[sc->id].syscon_arr =
+ jh7110_pll2_syscon_freq;
+ sc->pll_data[sc->id].syscon_nitems =
+ nitems(jh7110_pll2_syscon_freq);
+ PLL_MASK_FILL(2);
+ PLL_SHIFT_FILL(2);
+ PLL_OFFSET_FILL(sc->id, 5, 5, 5, 6, 7, 6);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/dev/clk/starfive/jh7110_clk_sys.c b/sys/dev/clk/starfive/jh7110_clk_sys.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/starfive/jh7110_clk_sys.c
@@ -0,0 +1,293 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
+ * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
+ * Copyright (c) 2022 Mitchell Horne <mhorne@FreeBSD.org>
+ * Copyright (c) 2024 Jari Sihvola <jsihv@gmx.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Clocks for JH7110 SYS group. PLL driver must be attached before this. */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/fbio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <dev/clk/clk.h>
+#include <dev/clk/clk_div.h>
+#include <dev/clk/clk_gate.h>
+#include <dev/clk/clk_link.h>
+#include <dev/clk/clk_mux.h>
+
+#include <dev/fdt/simplebus.h>
+#include <dev/hwreset/hwreset.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dt-bindings/clock/starfive,jh7110-crg.h>
+
+#include <dev/clk/starfive/jh7110_clk.h>
+
+#include "clkdev_if.h"
+#include "hwreset_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ { "starfive,jh7110-syscrg", 1 },
+ { NULL, 0 }
+};
+
+static struct resource_spec res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE },
+ RESOURCE_SPEC_END
+};
+
+/* parents for non-pll SYS clocks */
+static const char *cpu_root_p[] = { "osc", "pll0_out" };
+static const char *cpu_core_p[] = { "cpu_root" };
+static const char *cpu_bus_p[] = { "cpu_core" };
+static const char *perh_root_p[] = { "pll0_out", "pll2_out" };
+static const char *bus_root_p[] = { "osc", "pll2_out" };
+
+static const char *apb_bus_p[] = { "stg_axiahb" };
+static const char *apb0_p[] = { "apb_bus" };
+static const char *u0_sys_iomux_apb_p[] = { "apb_bus" };
+static const char *stg_axiahb_p[] = { "axi_cfg0" };
+static const char *ahb0_p[] = { "stg_axiahb" };
+static const char *axi_cfg0_p[] = { "bus_root" };
+
+static const char *u0_dw_uart_clk_apb_p[] = { "apb0" };
+static const char *u0_dw_uart_clk_core_p[] = { "osc" };
+static const char *u0_dw_sdio_clk_ahb_p[] = { "ahb0" };
+static const char *u0_dw_sdio_clk_sdcard_p[] = { "axi_cfg0" };
+static const char *u1_dw_uart_clk_apb_p[] = { "apb0" };
+static const char *u1_dw_uart_clk_core_p[] = { "osc" };
+static const char *u1_dw_sdio_clk_ahb_p[] = { "ahb0" };
+static const char *u1_dw_sdio_clk_sdcard_p[] = { "axi_cfg0" };
+static const char *u2_dw_uart_clk_apb_p[] = { "apb0" };
+static const char *u2_dw_uart_clk_core_p[] = { "osc" };
+static const char *u3_dw_uart_clk_apb_p[] = { "apb0" };
+static const char *u3_dw_uart_clk_core_p[] = { "perh_root" };
+
+static const char *gmac_src_p[] = { "pll0_out" };
+static const char *gmac_phy_p[] = { "gmac_src" };
+static const char *gmac0_gtxclk_p[] = { "pll0_out" };
+static const char *gmac0_ptp_p[] = { "gmac_src" };
+static const char *gmac0_gtxc_p[] = { "gmac0_gtxclk" };
+static const char *gmac1_gtxclk_p[] = { "pll0_out" };
+static const char *gmac1_gtxc_p[] = { "gmac1_gtxclk" };
+static const char *gmac1_rmii_rtx_p[] = { "gmac1_rmii_refin" };
+static const char *gmac1_axi_p[] = { "stg_axiahb" };
+static const char *gmac1_ahb_p[] = { "ahb0" };
+static const char *gmac1_ptp_p[] = { "gmac_src" };
+static const char *gmac1_tx_inv_p[] = { "gmac1_tx" };
+static const char *gmac1_tx_p[] = { "gmac1_gtxclk", "gmac1_rmii_rtx" };
+static const char *gmac1_rx_p[] = { "gmac1_rgmii_rxin", "gmac1_rmii_rtx" };
+static const char *gmac1_rx_inv_p[] = { "gmac1_rx" };
+
+/* non-pll SYS clocks */
+static const struct jh7110_clk_def sys_clks[] = {
+ JH7110_MUX(JH7110_SYSCLK_CPU_ROOT, "cpu_root", cpu_root_p),
+ JH7110_DIV(JH7110_SYSCLK_CPU_CORE, "cpu_core", cpu_core_p, 7),
+ JH7110_DIV(JH7110_SYSCLK_CPU_BUS, "cpu_bus", cpu_bus_p, 2),
+ JH7110_GATEDIV(JH7110_SYSCLK_PERH_ROOT, "perh_root", perh_root_p, 2),
+ JH7110_MUX(JH7110_SYSCLK_BUS_ROOT, "bus_root", bus_root_p),
+
+ JH7110_GATE(JH7110_SYSCLK_APB0, "apb0", apb0_p),
+ JH7110_GATE(JH7110_SYSCLK_IOMUX_APB, "u0_sys_iomux_apb",
+ u0_sys_iomux_apb_p),
+ JH7110_GATE(JH7110_SYSCLK_UART0_APB, "u0_dw_uart_clk_apb",
+ u0_dw_uart_clk_apb_p),
+ JH7110_GATE(JH7110_SYSCLK_UART0_CORE, "u0_dw_uart_clk_core",
+ u0_dw_uart_clk_core_p),
+ JH7110_GATE(JH7110_SYSCLK_UART1_APB, "u1_dw_uart_clk_apb",
+ u1_dw_uart_clk_apb_p),
+ JH7110_GATE(JH7110_SYSCLK_UART1_CORE, "u1_dw_uart_clk_core",
+ u1_dw_uart_clk_core_p),
+ JH7110_GATE(JH7110_SYSCLK_UART2_APB, "u2_dw_uart_clk_apb",
+ u2_dw_uart_clk_apb_p),
+ JH7110_GATE(JH7110_SYSCLK_UART2_CORE, "u2_dw_uart_clk_core",
+ u2_dw_uart_clk_core_p),
+ JH7110_GATE(JH7110_SYSCLK_UART3_APB, "u3_dw_uart_clk_apb",
+ u3_dw_uart_clk_apb_p),
+ JH7110_GATE(JH7110_SYSCLK_UART3_CORE, "u3_dw_uart_clk_core",
+ u3_dw_uart_clk_core_p),
+
+ JH7110_DIV(JH7110_SYSCLK_AXI_CFG0, "axi_cfg0", axi_cfg0_p, 3),
+ JH7110_DIV(JH7110_SYSCLK_STG_AXIAHB, "stg_axiahb", stg_axiahb_p, 2),
+ JH7110_GATE(JH7110_SYSCLK_AHB0, "ahb0", ahb0_p),
+ JH7110_DIV(JH7110_SYSCLK_APB_BUS, "apb_bus", apb_bus_p, 8),
+
+ JH7110_GATE(JH7110_SYSCLK_SDIO0_AHB, "u0_dw_sdio_clk_ahb",
+ u0_dw_sdio_clk_ahb_p),
+ JH7110_GATE(JH7110_SYSCLK_SDIO1_AHB, "u1_dw_sdio_clk_ahb",
+ u1_dw_sdio_clk_ahb_p),
+ JH7110_GATEDIV(JH7110_SYSCLK_SDIO0_SDCARD, "u0_dw_sdio_clk_sdcard",
+ u0_dw_sdio_clk_sdcard_p, 15),
+ JH7110_GATEDIV(JH7110_SYSCLK_SDIO1_SDCARD, "u1_dw_sdio_clk_sdcard",
+ u1_dw_sdio_clk_sdcard_p, 15),
+
+ JH7110_DIV(JH7110_SYSCLK_GMAC_SRC, "gmac_src", gmac_src_p, 7),
+ JH7110_GATEDIV(JH7110_SYSCLK_GMAC0_GTXCLK, "gmac0_gtxclk",
+ gmac0_gtxclk_p, 15),
+ JH7110_GATEDIV(JH7110_SYSCLK_GMAC0_PTP, "gmac0_ptp", gmac0_ptp_p, 31),
+ JH7110_GATEDIV(JH7110_SYSCLK_GMAC_PHY, "gmac_phy", gmac_phy_p, 31),
+ JH7110_GATE(JH7110_SYSCLK_GMAC0_GTXC, "gmac0_gtxc", gmac0_gtxc_p),
+
+ JH7110_MUX(JH7110_SYSCLK_GMAC1_RX, "gmac1_rx", gmac1_rx_p),
+ JH7110_INV(JH7110_SYSCLK_GMAC1_RX_INV, "gmac1_rx_inv", gmac1_rx_inv_p),
+ JH7110_GATE(JH7110_SYSCLK_GMAC1_AHB, "gmac1_ahb", gmac1_ahb_p),
+ JH7110_DIV(JH7110_SYSCLK_GMAC1_GTXCLK, "gmac1_gtxclk",
+ gmac1_gtxclk_p, 15),
+ JH7110_GATEMUX(JH7110_SYSCLK_GMAC1_TX, "gmac1_tx", gmac1_tx_p),
+ JH7110_INV(JH7110_SYSCLK_GMAC1_TX_INV, "gmac1_tx_inv", gmac1_tx_inv_p),
+ JH7110_GATEDIV(JH7110_SYSCLK_GMAC1_PTP, "gmac1_ptp", gmac1_ptp_p, 31),
+ JH7110_GATE(JH7110_SYSCLK_GMAC1_AXI, "gmac1_axi", gmac1_axi_p),
+ JH7110_GATE(JH7110_SYSCLK_GMAC1_GTXC, "gmac1_gtxc", gmac1_gtxc_p),
+ JH7110_DIV(JH7110_SYSCLK_GMAC1_RMII_RTX, "gmac1_rmii_rtx",
+ gmac1_rmii_rtx_p, 30),
+};
+
+static int
+jh7110_clk_sys_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "StarFive JH7110 SYS clock generator");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jh7110_clk_sys_attach(device_t dev)
+{
+ struct jh7110_clkgen_softc *sc;
+ int i, error;
+
+ sc = device_get_softc(dev);
+ sc->dev_flag = JH7110_CLK_SYS;
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ /* Allocate memory groups */
+
+ error = bus_alloc_resources(dev, res_spec, &sc->mem_res);
+ if (error) {
+ device_printf(dev,
+ "Couldn't allocate resources, error %d\n", error);
+ return (ENXIO);
+ }
+
+ /* Create clock domain */
+
+ sc->clkdom = clkdom_create(dev);
+ if (sc->clkdom == NULL) {
+ device_printf(dev, "Couldn't create clkdom\n");
+ return (ENXIO);
+ }
+
+ /* Registering clocks */
+
+ for (i = 0; i < nitems(sys_clks); i++) {
+ error = jh7110_clk_register(sc->clkdom, &sys_clks[i]);
+ if (error != 0) {
+ device_printf(dev, "Couldn't register clock %s: %d\n",
+ sys_clks[i].clkdef.name, error);
+ return (ENXIO);
+ }
+ }
+
+ if (clkdom_finit(sc->clkdom) != 0)
+ panic("Cannot finalize clkdom initialization\n");
+
+ if (bootverbose)
+ clkdom_dump(sc->clkdom);
+
+ hwreset_register_ofw_provider(dev);
+
+ return (0);
+}
+
+static int
+jh7110_clk_sys_detach(device_t dev)
+{
+ /* Detach not supported */
+ return (EACCES);
+}
+
+static void
+jh7110_clk_sys_device_lock(device_t dev)
+{
+ struct jh7110_clkgen_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock(&sc->mtx);
+}
+
+static void
+jh7110_clk_sys_device_unlock(device_t dev)
+{
+ struct jh7110_clkgen_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_unlock(&sc->mtx);
+}
+
+static device_method_t jh7110_clk_sys_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jh7110_clk_sys_probe),
+ DEVMETHOD(device_attach, jh7110_clk_sys_attach),
+ DEVMETHOD(device_detach, jh7110_clk_sys_detach),
+
+ /* clkdev interface */
+ DEVMETHOD(clkdev_device_lock, jh7110_clk_sys_device_lock),
+ DEVMETHOD(clkdev_device_unlock, jh7110_clk_sys_device_unlock),
+
+ /* Reset interface */
+ DEVMETHOD(hwreset_assert, jh7110_reset_assert),
+ DEVMETHOD(hwreset_is_asserted, jh7110_reset_is_asserted),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(jh7110_clk_sys, jh7110_clk_sys_driver, jh7110_clk_sys_methods,
+ sizeof(struct jh7110_clkgen_softc));
+EARLY_DRIVER_MODULE(jh7110_clk_sys, simplebus, jh7110_clk_sys_driver, 0, 0,
+ BUS_PASS_BUS + BUS_PASS_ORDER_LATE);
+MODULE_VERSION(jh7110_clk_sys, 1);

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 18, 12:44 AM (17 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31688443
Default Alt Text
D43037.id135253.diff (46 KB)

Event Timeline