Page MenuHomeFreeBSD

D55907.id173837.diff
No OneTemporary

D55907.id173837.diff

diff --git a/sys/dev/clk/broadcom/bcm_clk_periph.h b/sys/dev/clk/broadcom/bcm_clk_periph.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/broadcom/bcm_clk_periph.h
@@ -0,0 +1,60 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Perdixky <3293789706@qq.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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#ifndef BCM_CLK_PERIPH_H
+#define BCM_CLK_PERIPH_H
+
+#include <sys/types.h>
+
+#include <dev/clk/clk.h>
+
+struct bcm_clk_periph_def {
+ struct clknode_init_def clkdef;
+
+ uint32_t ctl_offset;
+ uint32_t div_offset;
+
+ uint32_t mash_shift;
+ uint32_t mash_width;
+
+ uint32_t busy_shift;
+
+ uint32_t enable_shift;
+
+ uint32_t src_shift;
+ uint32_t src_width;
+
+ uint32_t div_int_shift;
+ uint32_t div_int_width;
+ uint32_t div_frac_shift;
+ uint32_t div_frac_width;
+};
+
+int bcm_clk_periph_register(struct clkdom *clkdom,
+ const struct bcm_clk_periph_def *clkdef);
+
+#endif /* BCM_CLK_PERIPH_H */
diff --git a/sys/dev/clk/broadcom/bcm_clk_periph.c b/sys/dev/clk/broadcom/bcm_clk_periph.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/broadcom/bcm_clk_periph.c
@@ -0,0 +1,266 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Perdixky <3293789706@qq.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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/clk/broadcom/bcm_clk_periph.h>
+#include <dev/clk/clk.h>
+
+#include "clkdev_if.h"
+
+struct bcm_clk_periph_sc {
+ uint32_t ctl_offset;
+ uint32_t div_offset;
+
+ uint32_t mash_shift;
+ uint32_t mash_mask;
+
+ uint32_t busy_shift;
+
+ uint32_t enable_shift;
+
+ uint32_t src_shift;
+ uint32_t src_mask;
+
+ uint32_t div_int_shift;
+ uint32_t div_int_mask;
+ uint32_t div_frac_shift;
+ uint32_t div_frac_mask;
+};
+
+#define WRITE4(_clk, _off, _val) \
+ CLKDEV_WRITE_4(clknode_get_device(_clk), (_off), (_val))
+#define READ4(_clk, _off, _val) \
+ CLKDEV_READ_4(clknode_get_device(_clk), (_off), (_val))
+
+#define LOCK(_clk) CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define UNLOCK(_clk) CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int
+bcm_clk_periph_init(struct clknode *clk, device_t dev)
+{
+ struct bcm_clk_periph_sc *sc;
+ uint32_t val, idx;
+
+ sc = clknode_get_softc(clk);
+
+ LOCK(clk);
+ READ4(clk, sc->ctl_offset, &val);
+ UNLOCK(clk);
+
+ idx = (val & sc->src_mask) >> sc->src_shift;
+ clknode_init_parent_idx(clk, idx);
+
+ return (0);
+}
+
+static int
+bcm_clk_ctl_periph_gate(struct clknode *clk, bool enable)
+{
+ struct bcm_clk_periph_sc *sc;
+ uint32_t val;
+
+ sc = clknode_get_softc(clk);
+
+ LOCK(clk);
+ READ4(clk, sc->ctl_offset, &val);
+ if (enable)
+ val |= (1u << sc->enable_shift);
+ else
+ val &= ~(1u << sc->enable_shift);
+ WRITE4(clk, sc->ctl_offset, val);
+ UNLOCK(clk);
+
+ return (0);
+}
+
+static uint32_t
+bcm_clk_choose_div(struct bcm_clk_periph_sc *sc, uint64_t fin, uint64_t *fout)
+{
+ uint64_t div, best;
+ uint32_t min_div, max_div;
+
+ div = ((fin << 12) + (*fout / 2)) / *fout;
+ /* MASH typically requires a minimum divider of 2. */
+ min_div = (sc->mash_mask != 0) ? (2u << 12) : (1u << 12);
+ max_div = (sc->div_int_mask | sc->div_frac_mask);
+ if (div < min_div)
+ div = min_div;
+ if (div > max_div)
+ div = max_div;
+ best = (fin << 12) / div;
+ *fout = best;
+ return ((uint32_t)div);
+}
+
+static int
+bcm_clk_wait_while_busy(struct clknode *clk, struct bcm_clk_periph_sc *sc)
+{
+ uint32_t val;
+ int retry;
+
+ for (retry = 0; retry < 50; retry++) {
+ READ4(clk, sc->ctl_offset, &val);
+ if ((val & (1u << sc->busy_shift)) == 0)
+ return (0);
+ DELAY(1);
+ }
+ return (ETIMEDOUT);
+}
+
+static int
+bcm_clk_periph_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct bcm_clk_periph_sc *sc;
+ uint64_t req;
+ uint32_t ctl, div;
+ bool was_enabled;
+ int rv;
+
+ sc = clknode_get_softc(clk);
+ req = *fout;
+ div = bcm_clk_choose_div(sc, fparent, fout);
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ *stop = 1;
+ return (0);
+ }
+ if ((*fout < req) && ((flags & CLK_SET_ROUND_DOWN) == 0))
+ return (ERANGE);
+ if ((*fout > req) && ((flags & CLK_SET_ROUND_UP) == 0))
+ return (ERANGE);
+
+ /* Disable the clock and wait for BUSY to clear to avoid glitches. */
+ LOCK(clk);
+ READ4(clk, sc->ctl_offset, &ctl);
+ was_enabled = ((ctl & (1u << sc->enable_shift)) != 0);
+ if (was_enabled) {
+ ctl &= ~(1u << sc->enable_shift);
+ WRITE4(clk, sc->ctl_offset, ctl);
+ }
+
+ rv = bcm_clk_wait_while_busy(clk, sc);
+ if (rv != 0) {
+ UNLOCK(clk);
+ return (rv);
+ }
+
+ READ4(clk, sc->ctl_offset, &ctl);
+ WRITE4(clk, sc->div_offset, div);
+
+ if (was_enabled)
+ ctl |= (1u << sc->enable_shift);
+
+ ctl &= ~sc->mash_mask;
+ if (div & sc->div_frac_mask)
+ ctl |= (2u << sc->mash_shift);
+
+ WRITE4(clk, sc->ctl_offset, ctl);
+ UNLOCK(clk);
+
+ *stop = 1;
+ return (0);
+}
+
+static int
+bcm_clk_periph_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct bcm_clk_periph_sc *sc;
+ uint32_t div;
+
+ sc = clknode_get_softc(clk);
+
+ LOCK(clk);
+ READ4(clk, sc->div_offset, &div);
+ UNLOCK(clk);
+
+ /* Extract the full integer+fractional divider field (12.12
+ * fixed-point). */
+ div &= (sc->div_int_mask | sc->div_frac_mask);
+ if (div == 0)
+ return (0);
+
+ *freq = (*freq << 12) / div;
+ return (0);
+}
+
+static clknode_method_t bcm_clk_periph_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, bcm_clk_periph_init),
+ CLKNODEMETHOD(clknode_set_gate, bcm_clk_ctl_periph_gate),
+ CLKNODEMETHOD(clknode_recalc_freq, bcm_clk_periph_recalc),
+ CLKNODEMETHOD(clknode_set_freq, bcm_clk_periph_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(bcm_clk_periph_clknode, bcm_clk_periph_clknode_class,
+ bcm_clk_periph_clknode_methods, sizeof(struct bcm_clk_periph_sc),
+ clknode_class);
+
+int
+bcm_clk_periph_register(struct clkdom *clkdom,
+ const struct bcm_clk_periph_def *clkdef)
+{
+ struct clknode *clk;
+ struct bcm_clk_periph_sc *sc;
+
+ clk = clknode_create(clkdom, &bcm_clk_periph_clknode_class,
+ &clkdef->clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+
+ sc->ctl_offset = clkdef->ctl_offset;
+ sc->div_offset = clkdef->div_offset;
+
+ sc->mash_shift = clkdef->mash_shift;
+ sc->mash_mask = ((1u << clkdef->mash_width) - 1) << sc->mash_shift;
+
+ sc->busy_shift = clkdef->busy_shift;
+
+ sc->enable_shift = clkdef->enable_shift;
+
+ sc->src_shift = clkdef->src_shift;
+ sc->src_mask = ((1u << clkdef->src_width) - 1) << sc->src_shift;
+
+ sc->div_int_shift = clkdef->div_int_shift;
+ sc->div_int_mask = ((1u << clkdef->div_int_width) - 1)
+ << sc->div_int_shift;
+ sc->div_frac_shift = clkdef->div_frac_shift;
+ sc->div_frac_mask = ((1u << clkdef->div_frac_width) - 1)
+ << sc->div_frac_shift;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/dev/clk/broadcom/bcm_cprman.h b/sys/dev/clk/broadcom/bcm_cprman.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/broadcom/bcm_cprman.h
@@ -0,0 +1,58 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Perdixky <3293789706@qq.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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#ifndef BCM_CPRMAN_H
+#define BCM_CPRMAN_H
+
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/mutex.h>
+
+#include <dev/clk/broadcom/bcm_clk_periph.h>
+
+struct bcm_cprman_clk_data {
+ const struct bcm_clk_periph_def *periph_clks;
+ size_t periph_clk_cnt;
+};
+
+struct bcm_cprman_softc {
+ device_t dev;
+ struct resource *res;
+ struct clkdom *clkdom;
+ struct mtx mtx;
+ const struct bcm_cprman_clk_data *clk_data;
+};
+
+int bcm_cprman_attach(device_t dev);
+
+/*
+ * Base kobj class that provides the clkdev interface (read/write/lock).
+ * Chip-specific drivers inherit from this via DEFINE_CLASS_1.
+ */
+DECLARE_CLASS(bcm_cprman_driver);
+
+#endif /* BCM_CPRMAN_H */
diff --git a/sys/dev/clk/broadcom/bcm_cprman.c b/sys/dev/clk/broadcom/bcm_cprman.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/broadcom/bcm_cprman.c
@@ -0,0 +1,192 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Perdixky <3293789706@qq.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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/clk/broadcom/bcm_cprman.h>
+#include <dev/clk/clk.h>
+
+#include "clkdev_if.h"
+
+#if 0
+#define dprintf(fmt, ...) device_printf(dev, "%s: " fmt, __func__, __VA_ARGS__)
+#else
+#define dprintf(...)
+#endif
+
+static struct resource_spec bcm_cprman_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 }
+};
+
+#define CPR_PASSWD 0x5a000000
+
+#define CPR_READ4(sc, reg) bus_read_4((sc)->res, (reg))
+#define CPR_WRITE4(sc, reg, val) \
+ bus_write_4((sc)->res, (reg), (val | CPR_PASSWD))
+
+static int
+bcm_cprman_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct bcm_cprman_softc *sc;
+
+ sc = device_get_softc(dev);
+ dprintf("offset=%lx write %x\n", addr, val);
+ CPR_WRITE4(sc, addr, val);
+ return (0);
+}
+
+static int
+bcm_cprman_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+ struct bcm_cprman_softc *sc;
+
+ sc = device_get_softc(dev);
+ *val = CPR_READ4(sc, addr);
+ dprintf("offset=%lx read %x\n", addr, *val);
+ return (0);
+}
+
+static int
+bcm_cprman_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
+{
+ struct bcm_cprman_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+
+ dprintf("offset=%lx clr: %x set: %x\n", addr, clr, set);
+ reg = CPR_READ4(sc, addr);
+ reg &= ~clr;
+ reg |= set;
+ return (bcm_cprman_write_4(dev, addr, reg));
+}
+
+static void
+bcm_cprman_device_lock(device_t dev)
+{
+ struct bcm_cprman_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_lock_spin(&sc->mtx);
+}
+
+static void
+bcm_cprman_device_unlock(device_t dev)
+{
+ struct bcm_cprman_softc *sc;
+
+ sc = device_get_softc(dev);
+ mtx_unlock_spin(&sc->mtx);
+}
+
+static int
+bcm_cprman_register_periph_clks(struct bcm_cprman_softc *sc)
+{
+ int error;
+ size_t i;
+
+ for (i = 0; i < sc->clk_data->periph_clk_cnt; i++) {
+ error = bcm_clk_periph_register(sc->clkdom,
+ &sc->clk_data->periph_clks[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+int
+bcm_cprman_attach(device_t dev)
+{
+ struct bcm_cprman_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ if (sc->clk_data == NULL) {
+ device_printf(dev, "missing clock data\n");
+ return (ENXIO);
+ }
+
+ if (bus_alloc_resources(dev, bcm_cprman_spec, &sc->res) != 0) {
+ device_printf(dev, "cannot allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_SPIN);
+
+ sc->clkdom = clkdom_create(dev);
+ if (sc->clkdom == NULL) {
+ device_printf(dev, "cannot create clkdom\n");
+ mtx_destroy(&sc->mtx);
+ bus_release_resources(dev, bcm_cprman_spec, &sc->res);
+ return (ENXIO);
+ }
+
+ error = bcm_cprman_register_periph_clks(sc);
+ if (error != 0) {
+ device_printf(dev, "cannot register peripheral clocks\n");
+ return (ENXIO);
+ }
+
+ if (clkdom_finit(sc->clkdom) != 0)
+ panic("cannot finalize clkdom initialization");
+
+ if (bootverbose)
+ clkdom_dump(sc->clkdom);
+
+ return (0);
+}
+
+static device_method_t bcm_cprman_methods[] = {
+ /* clkdev interface: called by clknode layer to access hardware */
+ DEVMETHOD(clkdev_write_4, bcm_cprman_write_4),
+ DEVMETHOD(clkdev_read_4, bcm_cprman_read_4),
+ DEVMETHOD(clkdev_modify_4, bcm_cprman_modify_4),
+ DEVMETHOD(clkdev_device_lock, bcm_cprman_device_lock),
+ DEVMETHOD(clkdev_device_unlock, bcm_cprman_device_unlock), DEVMETHOD_END
+};
+
+/*
+ * Base class: holds the clkdev methods. Chip-specific drivers
+ * (e.g. bcm2835_cprman) inherit from this via DEFINE_CLASS_1 and
+ * add their own device_probe / device_attach on top.
+ */
+DEFINE_CLASS_0(bcm_cprman, bcm_cprman_driver, bcm_cprman_methods,
+ sizeof(struct bcm_cprman_softc));

File Metadata

Mime Type
text/plain
Expires
Fri, Apr 3, 4:51 AM (17 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30709388
Default Alt Text
D55907.id173837.diff (16 KB)

Event Timeline