Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F152930900
D55908.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
25 KB
Referenced Files
None
Subscribers
None
D55908.id.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D55908: clk/broadcom: Add PLL and PLL channel clock nodes
Attached
Detach File
Event Timeline
Log In to Comment