Page MenuHomeFreeBSD

D55908.id.diff
No OneTemporary

D55908.id.diff

diff --git a/sys/dev/clk/broadcom/bcm_clk_pll.h b/sys/dev/clk/broadcom/bcm_clk_pll.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/broadcom/bcm_clk_pll.h
@@ -0,0 +1,140 @@
+/*-
+ * 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_PLL_H_
+#define _BCM_CLK_PLL_H_
+
+#include <sys/types.h>
+
+#include <dev/clk/clk.h>
+
+/* Flags for bcm_clk_pll_def.flags */
+#define BCM_CLK_PLL_HAVE_FB_PREDIV (1u << 0) /* bcm2835: ANA1 fb_prediv */
+#define BCM_CLK_PLL_HAVE_VCO_RANGE (1u << 1) /* bcm2711: ANA1 vco_range */
+
+struct bcm_clk_cm_pll_def {
+ uint32_t anarst_shift;
+
+ uint32_t hold_per_shift;
+ uint32_t load_per_shift;
+
+ uint32_t hold_core_shift;
+ uint32_t load_core_shift;
+
+ uint32_t hold_ccp2_shift;
+ uint32_t load_ccp2_shift;
+
+ uint32_t hold_sdi0_shift;
+ uint32_t load_sdi0_shift;
+};
+
+struct bcm_clk_a2w_pll_ctrl_def {
+ uint32_t prst_disable_shift;
+
+ uint32_t pwrdn_shift;
+
+ uint32_t pdiv_shift;
+ uint32_t pdiv_width;
+
+ uint32_t ndiv_shift;
+ uint32_t ndiv_width;
+};
+
+struct bcm_clk_a2w_pll_frac_def {
+ uint32_t frac_shift;
+ uint32_t frac_width;
+};
+
+struct bcm_clk_a2w_xosc_ctrl_def {
+ uint32_t enable_shift;
+};
+
+struct bcm_clk_a2w_pll_nan1_def {
+ uint32_t ki_shift;
+ uint32_t ki_width;
+
+ uint32_t kp_shift;
+ uint32_t kp_width;
+
+ /*
+ * WARN: these two fields below are used for different purposes on
+ * bcm2835 and bcm2711, and they may overlap with each other.
+ */
+
+ uint32_t fb_prediv_shift;
+
+ /* TODO: Program the bcm2711 VCO range field. */
+ uint32_t vco_range_shift;
+ uint32_t vco_range_width;
+};
+
+struct bcm_clk_a2w_pll_nan3_def {
+ uint32_t ka_shift;
+ uint32_t ka_width;
+};
+
+struct bcm_clk_cm_lock_def {
+ uint32_t flock_shift;
+};
+
+struct bcm_clk_pll_def {
+ struct clknode_init_def clkdef;
+
+ uint64_t min_freq;
+ uint64_t max_freq;
+
+ /* Only used for bcm2835. */
+ uint64_t max_fb_freq;
+
+ uint32_t a2w_xosc_ctrl_offset;
+ struct bcm_clk_a2w_xosc_ctrl_def a2w_xosc_ctrl;
+
+ uint32_t cm_offset;
+ struct bcm_clk_cm_pll_def cm;
+
+ uint32_t cm_lock_offset;
+ struct bcm_clk_cm_lock_def cm_lock;
+
+ uint32_t a2w_ctrl_offset;
+ struct bcm_clk_a2w_pll_ctrl_def a2w_ctrl;
+
+ uint32_t a2w_frac_offset;
+ struct bcm_clk_a2w_pll_frac_def a2w_frac;
+
+ uint32_t a2w_nan1_offset;
+ struct bcm_clk_a2w_pll_nan1_def a2w_nan1;
+
+ uint32_t a2w_nan3_offset;
+ struct bcm_clk_a2w_pll_nan3_def a2w_nan3;
+
+ uint32_t flags;
+};
+
+int bcm_clk_pll_register(struct clkdom *clkdom,
+ const struct bcm_clk_pll_def *clkdef);
+
+#endif /* _BCM_CLK_PLL_H_ */
diff --git a/sys/dev/clk/broadcom/bcm_clk_pll.c b/sys/dev/clk/broadcom/bcm_clk_pll.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/broadcom/bcm_clk_pll.c
@@ -0,0 +1,418 @@
+/*-
+ * 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>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/clk/broadcom/bcm_clk_pll.h>
+#include <dev/clk/clk.h>
+
+#include "clkdev_if.h"
+
+#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 MODIFY4(_clk, _off, _clr, _set) \
+ CLKDEV_MODIFY_4(clknode_get_device(_clk), (_off), (_clr), (_set))
+
+#define LOCK(_clk) CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define UNLOCK(_clk) CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+#define BCM_CLK_PLL_KI_VAL 2u
+#define BCM_CLK_PLL_KP_VAL 8u
+#define BCM_CLK_PLL_KA_VAL 2u
+
+struct bcm_clk_cm_pll_sc {
+ uint32_t anarst_shift;
+
+ /* TODO: Use these CM hold/load bits for PLL-level sequencing. */
+ uint32_t hold_per_shift;
+ uint32_t load_per_shift;
+
+ uint32_t hold_core_shift;
+ uint32_t load_core_shift;
+
+ uint32_t hold_ccp2_shift;
+ uint32_t load_ccp2_shift;
+
+ uint32_t hold_sdi0_shift;
+ uint32_t load_sdi0_shift;
+};
+
+struct bcm_clk_a2w_pll_ctrl_sc {
+ uint32_t prst_disable_shift;
+
+ uint32_t pwrdn_shift;
+
+ uint32_t pdiv_shift;
+ uint32_t pdiv_mask;
+
+ uint32_t ndiv_shift;
+ uint32_t ndiv_mask;
+};
+
+struct bcm_clk_a2w_pll_frac_sc {
+ uint32_t frac_shift;
+ uint32_t frac_mask;
+};
+
+struct bcm_clk_a2w_xosc_ctrl_sc {
+ uint32_t enable_mask;
+};
+
+struct bcm_clk_a2w_pll_nan1_sc {
+ uint32_t ki_shift;
+ uint32_t ki_mask;
+
+ uint32_t kp_shift;
+ uint32_t kp_mask;
+
+ /*
+ * WARN: these two fields below are used for different purposes on
+ * bcm2835 and bcm2711, and they may overlap with each other.
+ */
+
+ uint32_t fb_prediv_shift;
+ uint32_t fb_prediv_mask;
+
+ uint32_t vco_range_shift;
+ uint32_t vco_range_mask;
+};
+
+struct bcm_clk_a2w_pll_nan3_sc {
+ uint32_t ka_shift;
+ uint32_t ka_mask;
+};
+
+struct bcm_clk_cm_lock_sc {
+ uint32_t flock_shift;
+};
+
+struct bcm_clk_pll_sc {
+ struct bcm_clk_cm_pll_sc cm;
+ struct bcm_clk_cm_lock_sc cm_lock;
+ struct bcm_clk_a2w_pll_ctrl_sc a2w_ctrl;
+ struct bcm_clk_a2w_pll_frac_sc a2w_frac;
+ struct bcm_clk_a2w_xosc_ctrl_sc a2w_xosc_ctrl;
+ struct bcm_clk_a2w_pll_nan1_sc a2w_nan1;
+ struct bcm_clk_a2w_pll_nan3_sc a2w_nan3;
+
+ uint64_t min_freq;
+ uint64_t max_freq;
+
+ uint64_t max_fb_freq;
+
+ uint32_t a2w_xosc_ctrl_offset;
+ uint32_t cm_offset;
+ uint32_t cm_lock_offset;
+ uint32_t a2w_ctrl_offset;
+ uint32_t a2w_frac_offset;
+ uint32_t a2w_nan1_offset;
+ uint32_t a2w_nan3_offset;
+
+ uint32_t flags;
+};
+
+static int
+bcm_clk_pll_init(struct clknode *clk, device_t dev)
+{
+ struct bcm_clk_pll_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ LOCK(clk);
+ MODIFY4(clk, sc->a2w_nan1_offset,
+ (sc->a2w_nan1.ki_mask << sc->a2w_nan1.ki_shift) |
+ (sc->a2w_nan1.kp_mask << sc->a2w_nan1.kp_shift),
+ ((BCM_CLK_PLL_KI_VAL & sc->a2w_nan1.ki_mask) <<
+ sc->a2w_nan1.ki_shift) |
+ ((BCM_CLK_PLL_KP_VAL & sc->a2w_nan1.kp_mask) <<
+ sc->a2w_nan1.kp_shift));
+ MODIFY4(clk, sc->a2w_nan3_offset,
+ sc->a2w_nan3.ka_mask << sc->a2w_nan3.ka_shift,
+ (BCM_CLK_PLL_KA_VAL & sc->a2w_nan3.ka_mask) <<
+ sc->a2w_nan3.ka_shift);
+ UNLOCK(clk);
+
+ clknode_init_parent_idx(clk, 0);
+
+ return (0);
+}
+
+static int
+bcm_clk_pll_wait_while_not_lock(struct clknode *clk, struct bcm_clk_pll_sc *sc)
+{
+ uint32_t val;
+ int timeout;
+
+ timeout = 1000;
+ while (timeout > 0) {
+ READ4(clk, sc->cm_lock_offset, &val);
+ if ((val & (1u << sc->cm_lock.flock_shift)) != 0)
+ return (0);
+
+ DELAY(10);
+ timeout--;
+ }
+
+ return (ETIMEDOUT);
+}
+
+static int
+bcm_clk_pll_set_gate(struct clknode *clk, bool enable)
+{
+ struct bcm_clk_pll_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ LOCK(clk);
+ if (enable) {
+ MODIFY4(clk, sc->a2w_xosc_ctrl_offset, 0, sc->a2w_xosc_ctrl.enable_mask);
+ MODIFY4(clk, sc->a2w_ctrl_offset, 1u << sc->a2w_ctrl.pwrdn_shift, 0);
+ MODIFY4(clk, sc->cm_offset, 1u << sc->cm.anarst_shift, 0);
+
+ if (bcm_clk_pll_wait_while_not_lock(clk, sc) != 0) {
+ UNLOCK(clk);
+ return (ETIMEDOUT);
+ }
+ MODIFY4(clk, sc->a2w_ctrl_offset, 0, 1u << sc->a2w_ctrl.prst_disable_shift);
+ } else {
+ MODIFY4(clk, sc->cm_offset, 0, 1u << sc->cm.anarst_shift);
+ MODIFY4(clk, sc->a2w_ctrl_offset, 0, 1u << sc->a2w_ctrl.pwrdn_shift);
+ }
+ UNLOCK(clk);
+
+ return (0);
+}
+
+static int
+bcm_clk_pll_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct bcm_clk_pll_sc *sc;
+ uint32_t pdiv, ndiv, frac, ctrl;
+
+ sc = clknode_get_softc(clk);
+
+ LOCK(clk);
+ READ4(clk, sc->a2w_ctrl_offset, &ctrl);
+ READ4(clk, sc->a2w_frac_offset, &frac);
+ UNLOCK(clk);
+
+ pdiv = (ctrl >> sc->a2w_ctrl.pdiv_shift) & sc->a2w_ctrl.pdiv_mask;
+ ndiv = (ctrl >> sc->a2w_ctrl.ndiv_shift) & sc->a2w_ctrl.ndiv_mask;
+ frac = (frac >> sc->a2w_frac.frac_shift) & sc->a2w_frac.frac_mask;
+
+ if (pdiv == 0)
+ return (0);
+
+ /*
+ * PLL output: Fout = Fosc * (ndiv + frac / frac_denom) / pdiv
+ * where frac_denom = 2^frac_width = frac_mask + 1.
+ */
+ *freq = (*freq * ((uint64_t)ndiv * (sc->a2w_frac.frac_mask + 1) + frac)) /
+ ((uint64_t)pdiv * (sc->a2w_frac.frac_mask + 1));
+
+ return (0);
+}
+
+static int
+bcm_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+ int flags, int *stop)
+{
+ struct bcm_clk_pll_sc *sc;
+ uint32_t ctrl, ana1, pdiv, ndiv, frac;
+ uint64_t frac_denom, req;
+ bool use_fb_prediv, was_using_fb_prediv, do_ana1_setup_first;
+
+ sc = clknode_get_softc(clk);
+ if (*fout < sc->min_freq) {
+ if ((flags & CLK_SET_ROUND_UP) == 0)
+ return (ERANGE);
+ *fout = sc->min_freq;
+ }
+ if (*fout > sc->max_freq) {
+ if ((flags & CLK_SET_ROUND_DOWN) == 0)
+ return (ERANGE);
+ *fout = sc->max_freq;
+ }
+
+ /*
+ * Read the current pdiv from hardware; we keep it fixed and only
+ * adjust ndiv + frac to hit the requested frequency.
+ */
+ LOCK(clk);
+ READ4(clk, sc->a2w_ctrl_offset, &ctrl);
+ READ4(clk, sc->a2w_nan1_offset, &ana1);
+ UNLOCK(clk);
+
+ pdiv = (ctrl >> sc->a2w_ctrl.pdiv_shift) & sc->a2w_ctrl.pdiv_mask;
+ if (pdiv == 0)
+ pdiv = 1;
+
+ /* If fout > max_fb_freq, we set fb_prediv flag. */
+ if ((sc->flags & BCM_CLK_PLL_HAVE_FB_PREDIV) && (*fout > sc->max_fb_freq))
+ use_fb_prediv = true;
+ else
+ use_fb_prediv = false;
+
+ /*
+ * Fout = Fosc * (ndiv + frac / frac_denom) * fb_factor / pdiv
+ * where fb_factor = 2 when fb_prediv is enabled, else 1.
+ *
+ * So we solve ndiv + frac / frac_denom against:
+ * target = Fout / fb_factor
+ */
+ frac_denom = (uint64_t)sc->a2w_frac.frac_mask + 1;
+ req = ((*fout >> use_fb_prediv) * pdiv * frac_denom + fparent / 2) / fparent;
+ ndiv = (uint32_t)(req / frac_denom);
+ frac = (uint32_t)(req % frac_denom);
+
+ /* Clamp ndiv to the hardware field width. */
+ if (ndiv > sc->a2w_ctrl.ndiv_mask)
+ ndiv = sc->a2w_ctrl.ndiv_mask;
+
+ /* Compute the frequency we will actually produce. */
+ *fout = (fparent * ((uint64_t)ndiv * frac_denom + frac) << use_fb_prediv) /
+ ((uint64_t)pdiv * frac_denom);
+
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ *stop = 1;
+ return (0);
+ }
+
+ /* We should prevent intermediate states with excessively high frequencies. */
+ was_using_fb_prediv = ((sc->flags & BCM_CLK_PLL_HAVE_FB_PREDIV) != 0) &&
+ ((ana1 & (1u << sc->a2w_nan1.fb_prediv_shift)) != 0);
+
+ if (use_fb_prediv && !was_using_fb_prediv)
+ do_ana1_setup_first = false;
+ else
+ do_ana1_setup_first = true;
+
+ LOCK(clk);
+ if (!do_ana1_setup_first) {
+ MODIFY4(clk, sc->a2w_ctrl_offset,
+ sc->a2w_ctrl.ndiv_mask << sc->a2w_ctrl.ndiv_shift,
+ (ndiv & sc->a2w_ctrl.ndiv_mask) << sc->a2w_ctrl.ndiv_shift);
+ MODIFY4(clk, sc->a2w_frac_offset,
+ sc->a2w_frac.frac_mask << sc->a2w_frac.frac_shift,
+ (frac & sc->a2w_frac.frac_mask) << sc->a2w_frac.frac_shift);
+ }
+
+ MODIFY4(clk, sc->a2w_nan1_offset,
+ sc->a2w_nan1.fb_prediv_mask << sc->a2w_nan1.fb_prediv_shift,
+ (use_fb_prediv ? 1u : 0) << sc->a2w_nan1.fb_prediv_shift);
+
+ if (do_ana1_setup_first) {
+ MODIFY4(clk, sc->a2w_ctrl_offset,
+ sc->a2w_ctrl.ndiv_mask << sc->a2w_ctrl.ndiv_shift,
+ (ndiv & sc->a2w_ctrl.ndiv_mask) << sc->a2w_ctrl.ndiv_shift);
+ MODIFY4(clk, sc->a2w_frac_offset,
+ sc->a2w_frac.frac_mask << sc->a2w_frac.frac_shift,
+ (frac & sc->a2w_frac.frac_mask) << sc->a2w_frac.frac_shift);
+ }
+ UNLOCK(clk);
+
+ *stop = 1;
+ return (0);
+}
+
+static clknode_method_t bcm_clk_pll_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, bcm_clk_pll_init),
+ CLKNODEMETHOD(clknode_set_gate, bcm_clk_pll_set_gate),
+ CLKNODEMETHOD(clknode_recalc_freq, bcm_clk_pll_recalc),
+ CLKNODEMETHOD(clknode_set_freq, bcm_clk_pll_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(bcm_clk_pll_clknode, bcm_clk_pll_clknode_class,
+ bcm_clk_pll_clknode_methods, sizeof(struct bcm_clk_pll_sc), clknode_class);
+
+int
+bcm_clk_pll_register(struct clkdom *clkdom,
+ const struct bcm_clk_pll_def *clkdef)
+{
+ struct bcm_clk_pll_sc *sc;
+ struct clknode *clk;
+
+ clk = clknode_create(clkdom, &bcm_clk_pll_clknode_class,
+ &clkdef->clkdef);
+ if (clk == NULL)
+ return (ENOMEM);
+
+ sc = clknode_get_softc(clk);
+ sc->min_freq = clkdef->min_freq;
+ sc->max_freq = clkdef->max_freq;
+ sc->max_fb_freq = clkdef->max_fb_freq;
+ sc->a2w_xosc_ctrl_offset = clkdef->a2w_xosc_ctrl_offset;
+ sc->a2w_xosc_ctrl.enable_mask = 1u << clkdef->a2w_xosc_ctrl.enable_shift;
+ sc->cm_offset = clkdef->cm_offset;
+ sc->cm.anarst_shift = clkdef->cm.anarst_shift;
+ sc->cm.hold_per_shift = clkdef->cm.hold_per_shift;
+ sc->cm.load_per_shift = clkdef->cm.load_per_shift;
+ sc->cm.hold_core_shift = clkdef->cm.hold_core_shift;
+ sc->cm.load_core_shift = clkdef->cm.load_core_shift;
+ sc->cm.hold_ccp2_shift = clkdef->cm.hold_ccp2_shift;
+ sc->cm.load_ccp2_shift = clkdef->cm.load_ccp2_shift;
+ sc->cm.hold_sdi0_shift = clkdef->cm.hold_sdi0_shift;
+ sc->cm.load_sdi0_shift = clkdef->cm.load_sdi0_shift;
+
+ sc->cm_lock_offset = clkdef->cm_lock_offset;
+ sc->cm_lock.flock_shift = clkdef->cm_lock.flock_shift;
+
+ sc->a2w_ctrl_offset = clkdef->a2w_ctrl_offset;
+ sc->a2w_ctrl.prst_disable_shift = clkdef->a2w_ctrl.prst_disable_shift;
+ sc->a2w_ctrl.pwrdn_shift = clkdef->a2w_ctrl.pwrdn_shift;
+ sc->a2w_ctrl.pdiv_shift = clkdef->a2w_ctrl.pdiv_shift;
+ sc->a2w_ctrl.pdiv_mask = ((1u << clkdef->a2w_ctrl.pdiv_width) - 1);
+ sc->a2w_ctrl.ndiv_shift = clkdef->a2w_ctrl.ndiv_shift;
+ sc->a2w_ctrl.ndiv_mask = ((1u << clkdef->a2w_ctrl.ndiv_width) - 1);
+
+ sc->a2w_frac_offset = clkdef->a2w_frac_offset;
+ sc->a2w_frac.frac_shift = clkdef->a2w_frac.frac_shift;
+ sc->a2w_frac.frac_mask = ((1u << clkdef->a2w_frac.frac_width) - 1);
+
+ sc->a2w_nan1_offset = clkdef->a2w_nan1_offset;
+ sc->a2w_nan1.ki_shift = clkdef->a2w_nan1.ki_shift;
+ sc->a2w_nan1.ki_mask = ((1u << clkdef->a2w_nan1.ki_width) - 1);
+ sc->a2w_nan1.kp_shift = clkdef->a2w_nan1.kp_shift;
+ sc->a2w_nan1.kp_mask = ((1u << clkdef->a2w_nan1.kp_width) - 1);
+
+ sc->a2w_nan3_offset = clkdef->a2w_nan3_offset;
+ sc->a2w_nan3.ka_shift = clkdef->a2w_nan3.ka_shift;
+ sc->a2w_nan3.ka_mask = ((1u << clkdef->a2w_nan3.ka_width) - 1);
+
+ sc->flags = clkdef->flags;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/dev/clk/broadcom/bcm_cprman.h b/sys/dev/clk/broadcom/bcm_clk_pll_channel.h
copy from sys/dev/clk/broadcom/bcm_cprman.h
copy to sys/dev/clk/broadcom/bcm_clk_pll_channel.h
--- a/sys/dev/clk/broadcom/bcm_cprman.h
+++ b/sys/dev/clk/broadcom/bcm_clk_pll_channel.h
@@ -25,34 +25,29 @@
* SUCH DAMAGE.
*/
-#ifndef BCM_CPRMAN_H
-#define BCM_CPRMAN_H
+#ifndef BCM_CLK_PLL_CHANNEL_H
+#define BCM_CLK_PLL_CHANNEL_H
#include <sys/types.h>
-#include <sys/bus.h>
-#include <sys/mutex.h>
-#include <dev/clk/broadcom/bcm_clk_periph.h>
+#include <dev/clk/clk.h>
-struct bcm_cprman_clk_data {
- const struct bcm_clk_periph_def *periph_clks;
- size_t periph_clk_cnt;
-};
+struct bcm_clk_pll_channel_def {
+ struct clknode_init_def clkdef;
-struct bcm_cprman_softc {
- device_t dev;
- struct resource *res;
- struct clkdom *clkdom;
- struct mtx mtx;
- const struct bcm_cprman_clk_data *clk_data;
-};
+ uint32_t cm_offset;
+ uint32_t hold_shift;
+ uint32_t load_shift;
-int bcm_cprman_attach(device_t dev);
+ uint32_t offset;
-/*
- * 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);
+ uint32_t disable_shift;
+
+ uint32_t div_shift;
+ uint32_t div_width;
+};
+
+int bcm_clk_pll_channel_register(struct clkdom *clkdom,
+ const struct bcm_clk_pll_channel_def *clkdef);
-#endif /* BCM_CPRMAN_H */
+#endif /* BCM_CLK_PLL_CHANNEL_H */
diff --git a/sys/dev/clk/broadcom/bcm_clk_pll_channel.c b/sys/dev/clk/broadcom/bcm_clk_pll_channel.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/clk/broadcom/bcm_clk_pll_channel.c
@@ -0,0 +1,202 @@
+/*-
+ * 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>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/clk/broadcom/bcm_clk_pll_channel.h>
+#include <dev/clk/clk.h>
+
+#include "clkdev_if.h"
+
+struct bcm_clk_pll_channel_sc {
+ uint32_t cm_offset;
+ uint32_t hold_shift;
+ uint32_t load_shift;
+
+ uint32_t offset;
+
+ uint32_t disable_shift;
+
+ uint32_t div_shift;
+ uint32_t div_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 MODIFY4(_clk, _off, _clr, _set) \
+ CLKDEV_MODIFY_4(clknode_get_device(_clk), (_off), (_clr), (_set))
+
+#define LOCK(_clk) CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define UNLOCK(_clk) CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int
+bcm_clk_pll_channel_init(struct clknode *clk, device_t dev)
+{
+ clknode_init_parent_idx(clk, 0);
+
+ return (0);
+}
+
+static int
+bcm_clk_pll_channel_gate(struct clknode *clk, bool enable)
+{
+ struct bcm_clk_pll_channel_sc *sc;
+
+ sc = clknode_get_softc(clk);
+
+ LOCK(clk);
+ if (enable) {
+ MODIFY4(clk, sc->offset, 1u << sc->disable_shift, 0);
+ MODIFY4(clk, sc->cm_offset, 1u << sc->hold_shift, 0);
+ } else {
+ MODIFY4(clk, sc->offset, 0, 1u << sc->disable_shift);
+ MODIFY4(clk, sc->cm_offset, 0, 1u << sc->hold_shift);
+ }
+ UNLOCK(clk);
+
+ return (0);
+}
+
+static int
+bcm_clk_pll_channel_calc_div(struct bcm_clk_pll_channel_sc *sc, uint64_t fin,
+ uint64_t *fout)
+{
+ uint64_t div, best;
+ uint32_t min_div, max_div;
+
+ div = (fin + (*fout / 2)) / *fout;
+ min_div = 1;
+ max_div = sc->div_mask;
+ if (div < min_div)
+ div = min_div;
+ else if (div > max_div)
+ div = max_div;
+
+ best = fin / div;
+
+ if (div == max_div)
+ div = 0;
+
+ *fout = best;
+ return ((uint32_t)div);
+}
+
+static int
+bcm_clk_pll_channel_set_freq(struct clknode *clk, uint64_t fparent,
+ uint64_t *fout, int flags, int *stop)
+{
+ struct bcm_clk_pll_channel_sc *sc;
+ uint64_t req;
+ uint32_t div;
+
+ sc = clknode_get_softc(clk);
+ req = *fout;
+ div = bcm_clk_pll_channel_calc_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);
+ MODIFY4(clk, sc->offset, sc->div_mask << sc->div_shift,
+ (div << sc->div_shift) & (sc->div_mask << sc->div_shift));
+ MODIFY4(clk, sc->cm_offset, 0, 1u << sc->load_shift);
+ MODIFY4(clk, sc->cm_offset, 1u << sc->hold_shift, 0);
+ UNLOCK(clk);
+
+ *stop = 1;
+ return (0);
+}
+
+static int
+bcm_clk_pll_channel_recalc(struct clknode *clk, uint64_t *freq)
+{
+ struct bcm_clk_pll_channel_sc *sc;
+ uint32_t div;
+
+ sc = clknode_get_softc(clk);
+
+ LOCK(clk);
+ READ4(clk, sc->offset, &div);
+ UNLOCK(clk);
+
+ div = (div >> sc->div_shift) & sc->div_mask;
+ if (div == 0)
+ div = sc->div_mask + 1;
+
+ *freq = *freq / div;
+ return (0);
+}
+
+static clknode_method_t bcm_clk_pll_channel_clknode_methods[] = {
+ /* Device interface */
+ CLKNODEMETHOD(clknode_init, bcm_clk_pll_channel_init),
+ CLKNODEMETHOD(clknode_set_gate, bcm_clk_pll_channel_gate),
+ CLKNODEMETHOD(clknode_recalc_freq, bcm_clk_pll_channel_recalc),
+ CLKNODEMETHOD(clknode_set_freq, bcm_clk_pll_channel_set_freq),
+ CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(bcm_clk_pll_channel_clknode, bcm_clk_pll_channel_clknode_class,
+ bcm_clk_pll_channel_clknode_methods, sizeof(struct bcm_clk_pll_channel_sc),
+ clknode_class);
+
+int
+bcm_clk_pll_channel_register(struct clkdom *clkdom,
+ const struct bcm_clk_pll_channel_def *clkdef)
+{
+ struct bcm_clk_pll_channel_sc *sc;
+ struct clknode *clk;
+
+ clk = clknode_create(clkdom, &bcm_clk_pll_channel_clknode_class,
+ &clkdef->clkdef);
+ if (clk == NULL)
+ return (ENOMEM);
+
+ sc = clknode_get_softc(clk);
+ sc->cm_offset = clkdef->cm_offset;
+ sc->hold_shift = clkdef->hold_shift;
+ sc->load_shift = clkdef->load_shift;
+ sc->offset = clkdef->offset;
+ sc->disable_shift = clkdef->disable_shift;
+ sc->div_shift = clkdef->div_shift;
+ sc->div_mask = (1u << clkdef->div_width) - 1;
+
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/dev/clk/broadcom/bcm_cprman.h b/sys/dev/clk/broadcom/bcm_cprman.h
--- a/sys/dev/clk/broadcom/bcm_cprman.h
+++ b/sys/dev/clk/broadcom/bcm_cprman.h
@@ -33,10 +33,18 @@
#include <sys/mutex.h>
#include <dev/clk/broadcom/bcm_clk_periph.h>
+#include <dev/clk/broadcom/bcm_clk_pll.h>
+#include <dev/clk/broadcom/bcm_clk_pll_channel.h>
struct bcm_cprman_clk_data {
const struct bcm_clk_periph_def *periph_clks;
size_t periph_clk_cnt;
+
+ const struct bcm_clk_pll_def *pll_clks;
+ size_t pll_clk_cnt;
+
+ const struct bcm_clk_pll_channel_def *pll_chan_clks;
+ size_t pll_chan_clk_cnt;
};
struct bcm_cprman_softc {
diff --git a/sys/dev/clk/broadcom/bcm_cprman.c b/sys/dev/clk/broadcom/bcm_cprman.c
--- a/sys/dev/clk/broadcom/bcm_cprman.c
+++ b/sys/dev/clk/broadcom/bcm_cprman.c
@@ -132,6 +132,38 @@
return (0);
}
+static int
+bcm_cprman_register_plls(struct bcm_cprman_softc *sc)
+{
+ int error;
+ size_t i;
+
+ for (i = 0; i < sc->clk_data->pll_clk_cnt; i++) {
+ error = bcm_clk_pll_register(sc->clkdom,
+ &sc->clk_data->pll_clks[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+bcm_cprman_register_pll_channels(struct bcm_cprman_softc *sc)
+{
+ int error;
+ size_t i;
+
+ for (i = 0; i < sc->clk_data->pll_chan_clk_cnt; i++) {
+ error = bcm_clk_pll_channel_register(sc->clkdom,
+ &sc->clk_data->pll_chan_clks[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
int
bcm_cprman_attach(device_t dev)
{
@@ -160,6 +192,18 @@
return (ENXIO);
}
+ error = bcm_cprman_register_plls(sc);
+ if (error != 0) {
+ device_printf(dev, "cannot register PLL clocks\n");
+ return (ENXIO);
+ }
+
+ error = bcm_cprman_register_pll_channels(sc);
+ if (error != 0) {
+ device_printf(dev, "cannot register PLL channel clocks\n");
+ return (ENXIO);
+ }
+
error = bcm_cprman_register_periph_clks(sc);
if (error != 0) {
device_printf(dev, "cannot register peripheral clocks\n");

File Metadata

Mime Type
text/plain
Expires
Sun, Apr 19, 3:44 AM (1 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31744661
Default Alt Text
D55908.id.diff (25 KB)

Event Timeline