git: e34a491b3562 - main - qcom_clk: add the qualcomm clock nodes for the IPQ4018

From: Adrian Chadd <adrian_at_FreeBSD.org>
Date: Mon, 27 Dec 2021 21:05:19 UTC
The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=e34a491b35626b4209ef0a195e85a03a1089c572

commit e34a491b35626b4209ef0a195e85a03a1089c572
Author:     Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2021-12-26 17:07:21 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2021-12-27 21:02:30 +0000

    qcom_clk: add the qualcomm clock nodes for the IPQ4018
    
    These clock nodes are used by the IPQ4018/IPQ4019 and derivatives.
    They're also used by other 32 and 64 bit qualcomm parts; so it's
    best to put these nodes here in a single qcom_clk driver and add
    to it as we grow new Qualcomm SoC support.
    
    Tested:
    
    * IPQ4018, boot
    
    Differential Revision: https://reviews.freebsd.org/D33665
---
 sys/arm/qualcomm/std.ipq4018            |   8 +
 sys/dev/qcom_clk/qcom_clk_apssdiv.c     | 287 ++++++++++++++
 sys/dev/qcom_clk/qcom_clk_apssdiv.h     |  48 +++
 sys/dev/qcom_clk/qcom_clk_branch2.c     | 290 ++++++++++++++
 sys/dev/qcom_clk/qcom_clk_branch2.h     |  70 ++++
 sys/dev/qcom_clk/qcom_clk_branch2_reg.h |  39 ++
 sys/dev/qcom_clk/qcom_clk_fdiv.c        | 115 ++++++
 sys/dev/qcom_clk/qcom_clk_fdiv.h        |  41 ++
 sys/dev/qcom_clk/qcom_clk_fepll.c       | 153 ++++++++
 sys/dev/qcom_clk/qcom_clk_fepll.h       |  45 +++
 sys/dev/qcom_clk/qcom_clk_freqtbl.c     |  56 +++
 sys/dev/qcom_clk/qcom_clk_freqtbl.h     |  44 +++
 sys/dev/qcom_clk/qcom_clk_rcg2.c        | 660 ++++++++++++++++++++++++++++++++
 sys/dev/qcom_clk/qcom_clk_rcg2.h        |  61 +++
 sys/dev/qcom_clk/qcom_clk_rcg2_reg.h    |  58 +++
 sys/dev/qcom_clk/qcom_clk_ro_div.c      | 153 ++++++++
 sys/dev/qcom_clk/qcom_clk_ro_div.h      |  49 +++
 17 files changed, 2177 insertions(+)

diff --git a/sys/arm/qualcomm/std.ipq4018 b/sys/arm/qualcomm/std.ipq4018
index 6676a896086e..38e561f4079c 100644
--- a/sys/arm/qualcomm/std.ipq4018
+++ b/sys/arm/qualcomm/std.ipq4018
@@ -8,6 +8,14 @@ arm/qualcomm/qcom_gcc_ipq4018.c		optional qcom_gcc_ipq4018
 arm/qualcomm/qcom_gcc_ipq4018_reset.c	optional qcom_gcc_ipq4018
 arm/qualcomm/qcom_gcc_ipq4018_clock.c	optional qcom_gcc_ipq4018
 
+dev/qcom_clk/qcom_clk_fepll.c		optional qcom_gcc_ipq4018
+dev/qcom_clk/qcom_clk_fdiv.c		optional qcom_gcc_ipq4018
+dev/qcom_clk/qcom_clk_apssdiv.c		optional qcom_gcc_ipq4018
+dev/qcom_clk/qcom_clk_freqtbl.c		optional qcom_gcc_ipq4018
+dev/qcom_clk/qcom_clk_rcg2.c		optional qcom_gcc_ipq4018
+dev/qcom_clk/qcom_clk_branch2.c		optional qcom_gcc_ipq4018
+dev/qcom_clk/qcom_clk_ro_div.c		optional qcom_gcc_ipq4018
+
 dev/qcom_tlmm/qcom_tlmm_debug.c		optional qcom_tlmm_ipq4018
 dev/qcom_tlmm/qcom_tlmm_ipq4018.c	optional qcom_tlmm_ipq4018
 dev/qcom_tlmm/qcom_tlmm_ipq4018_hw.c	optional qcom_tlmm_ipq4018
diff --git a/sys/dev/qcom_clk/qcom_clk_apssdiv.c b/sys/dev/qcom_clk/qcom_clk_apssdiv.c
new file mode 100644
index 000000000000..75216ba60172
--- /dev/null
+++ b/sys/dev/qcom_clk/qcom_clk_apssdiv.c
@@ -0,0 +1,287 @@
+/*-
+ * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>.
+ *
+ * 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/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include "qcom_clk_freqtbl.h"
+#include "qcom_clk_apssdiv.h"
+
+#include "clkdev_if.h"
+
+/*
+ * This is a combination gate, divisor/PLL configuration
+ * for the APSS CPU clock.
+ */
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, "cpufreq_dt: " msg);
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+struct qcom_clk_apssdiv_sc {
+	struct clknode *clknode;
+	uint32_t div_offset;
+	uint32_t div_width;
+	uint32_t div_shift;
+	uint32_t enable_offset;
+	uint32_t enable_shift;
+	const struct qcom_clk_freq_tbl *freq_tbl;
+};
+
+static uint64_t
+qcom_clk_apssdiv_calc_rate(struct clknode *clk, uint64_t freq, uint32_t cdiv)
+{
+	uint32_t pre_div;
+
+	/*
+	 * The divisor isn't a linear map with a linear pre-divisor.
+	 */
+	if (cdiv > 10) {
+		pre_div = (cdiv + 1) * 2;
+	} else {
+		pre_div = cdiv + 12;
+	}
+	/*
+	 * Multiplier is a fixed "2" here.
+	 */
+	return (freq * 2L) / pre_div;
+}
+
+static int
+qcom_clk_apssdiv_recalc(struct clknode *clk, uint64_t *freq)
+{
+	struct qcom_clk_apssdiv_sc *sc;
+	uint32_t reg, cdiv;
+
+	sc = clknode_get_softc(clk);
+
+	if (freq == NULL || *freq == 0) {
+		printf("%s: called; NULL or 0 frequency\n", __func__);
+		return (ENXIO);
+	}
+
+	CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
+	CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->div_offset, &reg);
+	CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
+	cdiv = (reg >> sc->div_shift) & ((1U << sc->div_width) - 1);
+
+	DPRINTF(clknode_get_device(sc->clknode),
+	    "%s: called; cdiv=0x%x, freq=%llu\n", __func__, cdiv, *freq);
+
+	*freq = qcom_clk_apssdiv_calc_rate(clk, *freq, cdiv);
+
+	DPRINTF(clknode_get_device(sc->clknode),
+	    "%s: called; freq is %llu\n", __func__, *freq);
+	return (0);
+}
+
+#if 0
+static bool
+qcom_clk_apssdiv_get_gate_locked(struct qcom_clk_apssdiv_sc *sc)
+{
+	uint32_t reg;
+
+	if (sc->enable_offset == 0)
+		return (false);
+
+	CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->enable_offset,
+	    &reg);
+
+	return (!! (reg & (1U << sc->enable_shift)));
+}
+#endif
+
+static int
+qcom_clk_apssdiv_init(struct clknode *clk, device_t dev)
+{
+	struct qcom_clk_apssdiv_sc *sc;
+
+	sc = clknode_get_softc(clk);
+
+	/*
+	 * There's only a single parent here for an fixed divisor,
+	 * so just set it to 0; the caller doesn't need to supply it.
+	 *
+	 * Note that the freqtbl entries have an upstream clock,
+	 * but the APSS div/gate only has a single upstream and we
+	 * don't program anything else specific in here.
+	 */
+	clknode_init_parent_idx(clk, 0);
+
+	return (0);
+}
+
+static int
+qcom_clk_apssdiv_set_gate(struct clknode *clk, bool enable)
+{
+	struct qcom_clk_apssdiv_sc *sc;
+	uint32_t reg;
+
+	sc = clknode_get_softc(clk);
+
+	if (sc->enable_offset == 0) {
+		return (ENXIO);
+	}
+
+	DPRINTF(clknode_get_device(sc->clknode),
+	    "%s: called; enable=%d\n", __func__, enable);
+
+	CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
+	CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->enable_offset,
+	    &reg);
+	if (enable) {
+		reg |= (1U << sc->enable_shift);
+	} else {
+		reg &= ~(1U << sc->enable_shift);
+	}
+	CLKDEV_WRITE_4(clknode_get_device(sc->clknode), sc->enable_offset,
+	    reg);
+	CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
+
+	return (0);
+}
+
+/*
+ * Set frequency
+ *
+ * fin - the parent frequency, if exists
+ * fout - starts as the requested frequency, ends with the configured
+ *        or dry-run frequency
+ * Flags - CLK_SET_DRYRUN, CLK_SET_ROUND_UP, CLK_SET_ROUND_DOWN
+ * retval - 0, ERANGE
+ */
+static int
+qcom_clk_apssdiv_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+    int flags, int *stop)
+{
+	const struct qcom_clk_freq_tbl *f;
+	struct qcom_clk_apssdiv_sc *sc;
+	uint64_t f_freq;
+	uint32_t reg;
+
+	sc = clknode_get_softc(clk);
+
+	/* There are no further PLLs to set in this chain */
+	*stop = 1;
+
+	/* Search the table for a suitable frequency */
+	f = qcom_clk_freq_tbl_lookup(sc->freq_tbl, *fout);
+	if (f == NULL) {
+		return (ERANGE);
+	}
+
+	/*
+	 * Calculate what the resultant frequency would be based on the
+	 * parent PLL.
+	 */
+	f_freq = qcom_clk_apssdiv_calc_rate(clk, fin, f->pre_div);
+
+	DPRINTF(clknode_get_device(sc->clknode),
+	    "%s: dryrun: %d, fin=%llu fout=%llu f_freq=%llu pre_div=%u"
+	    " target_freq=%llu\n",
+	    __func__,
+	    !! (flags & CLK_SET_DRYRUN),
+	    fin, *fout, f_freq, f->pre_div, f->freq);
+
+	if (flags & CLK_SET_DRYRUN) {
+		*fout = f_freq;
+		return (0);
+	}
+
+	/*
+	 * Program in the new pre-divisor.
+	 */
+	CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
+	CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->div_offset, &reg);
+	reg &= ~(((1U << sc->div_width) - 1) << sc->div_shift);
+	reg |= (f->pre_div << sc->div_shift);
+	CLKDEV_WRITE_4(clknode_get_device(sc->clknode), sc->div_offset, reg);
+	CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
+
+	/*
+	 * The linux driver notes there's no status/completion bit to poll.
+	 * So sleep for a bit and hope that's enough time for it to
+	 * settle.
+	 */
+	DELAY(1);
+
+	*fout = f_freq;
+
+	return (0);
+}
+
+static clknode_method_t qcom_clk_apssdiv_methods[] = {
+	/* Device interface */
+	CLKNODEMETHOD(clknode_init,		qcom_clk_apssdiv_init),
+	CLKNODEMETHOD(clknode_recalc_freq,	qcom_clk_apssdiv_recalc),
+	CLKNODEMETHOD(clknode_set_gate,		qcom_clk_apssdiv_set_gate),
+	CLKNODEMETHOD(clknode_set_freq,		qcom_clk_apssdiv_set_freq),
+	CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(qcom_clk_apssdiv, qcom_clk_apssdiv_class,
+    qcom_clk_apssdiv_methods, sizeof(struct qcom_clk_apssdiv_sc),
+    clknode_class);
+
+int
+qcom_clk_apssdiv_register(struct clkdom *clkdom,
+    struct qcom_clk_apssdiv_def *clkdef)
+{
+	struct clknode *clk;
+	struct qcom_clk_apssdiv_sc *sc;
+
+	clk = clknode_create(clkdom, &qcom_clk_apssdiv_class, &clkdef->clkdef);
+	if (clk == NULL)
+		return (1);
+
+	sc = clknode_get_softc(clk);
+	sc->clknode = clk;
+
+	sc->div_offset = clkdef->div_offset;
+	sc->div_width = clkdef->div_width;
+	sc->div_shift = clkdef->div_shift;
+	sc->freq_tbl = clkdef->freq_tbl;
+	sc->enable_offset = clkdef->enable_offset;
+	sc->enable_shift = clkdef->enable_shift;
+
+	clknode_register(clkdom, clk);
+
+	return (0);
+}
diff --git a/sys/dev/qcom_clk/qcom_clk_apssdiv.h b/sys/dev/qcom_clk/qcom_clk_apssdiv.h
new file mode 100644
index 000000000000..e1ff39a10ce0
--- /dev/null
+++ b/sys/dev/qcom_clk/qcom_clk_apssdiv.h
@@ -0,0 +1,48 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	__QCOM_CLK_APSS_H__
+#define	__QCOM_CLK_APSS_H__
+
+#include "qcom_clk_freqtbl.h"
+
+struct qcom_clk_apssdiv_def {
+	struct clknode_init_def clkdef;
+	uint32_t div_offset;
+	uint32_t div_width;
+	uint32_t div_shift;
+	uint32_t enable_offset;
+	uint32_t enable_shift;
+	const struct qcom_clk_freq_tbl *freq_tbl;
+};
+
+extern	int qcom_clk_apssdiv_register(struct clkdom *clkdom,
+	    struct qcom_clk_apssdiv_def *clkdef);
+
+#endif	/* __QCOM_CLK_APSS_H__ */
diff --git a/sys/dev/qcom_clk/qcom_clk_branch2.c b/sys/dev/qcom_clk/qcom_clk_branch2.c
new file mode 100644
index 000000000000..83f1e8fd888d
--- /dev/null
+++ b/sys/dev/qcom_clk/qcom_clk_branch2.c
@@ -0,0 +1,290 @@
+/*-
+ * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>.
+ *
+ * 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/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include "qcom_clk_branch2.h"
+#include "qcom_clk_branch2_reg.h"
+
+#include "clkdev_if.h"
+
+/*
+ * This is a combination gate/status and dynamic hardware clock gating with
+ * voting.
+ */
+
+#if 0
+#define DPRINTF(dev, msg...) device_printf(dev, msg);
+#else
+#define DPRINTF(dev, msg...)
+#endif
+
+struct qcom_clk_branch2_sc {
+	struct clknode *clknode;
+	uint32_t flags;
+	uint32_t enable_offset;
+	uint32_t enable_shift;
+	uint32_t hwcg_reg;
+	uint32_t hwcg_bit;
+	uint32_t halt_reg;
+	uint32_t halt_check_type;
+	bool halt_check_voted;
+};
+#if 0
+static bool
+qcom_clk_branch2_get_gate_locked(struct qcom_clk_branch2_sc *sc)
+{
+	uint32_t reg;
+
+	CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->enable_offset,
+	    &reg);
+
+	DPRINTF(clknode_get_device(sc->clknode),
+	    "%s: offset=0x%x, reg=0x%x\n", __func__,
+	    sc->enable_offset, reg);
+
+	return (!! (reg & (1U << sc->enable_shift)));
+}
+#endif
+
+static int
+qcom_clk_branch2_init(struct clknode *clk, device_t dev)
+{
+
+	clknode_init_parent_idx(clk, 0);
+
+	return (0);
+}
+
+static bool
+qcom_clk_branch2_in_hwcg_mode_locked(struct qcom_clk_branch2_sc *sc)
+{
+	uint32_t reg;
+
+	if (sc->hwcg_reg == 0)
+		return (false);
+	
+	CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->hwcg_reg,
+	    &reg);
+
+	return (!! (reg & (1U << sc->hwcg_bit)));
+}
+
+static bool
+qcom_clk_branch2_check_halt_locked(struct qcom_clk_branch2_sc *sc, bool enable)
+{
+	uint32_t reg;
+
+	CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->halt_reg, &reg);
+
+	if (enable) {
+		/*
+		 * The upstream Linux code is .. unfortunate.
+		 *
+		 * Here it says "return true if BRANCH_CLK_OFF is not set,
+		 * or if the status field = FSM_STATUS_ON AND
+		 * the clk_off field is 0.
+		 *
+		 * Which .. is weird, because I can't currently see
+		 * how we'd ever need to check FSM_STATUS_ON - the only
+		 * valid check for the FSM status also requires clk_off=0.
+		 */
+		return !! ((reg & QCOM_CLK_BRANCH2_CLK_OFF) == 0);
+	} else {
+		return !! (reg & QCOM_CLK_BRANCH2_CLK_OFF);
+	}
+}
+
+/*
+ * Check if the given type/voted flag match what is configured.
+ */
+static bool
+qcom_clk_branch2_halt_check_type(struct qcom_clk_branch2_sc *sc,
+    uint32_t type, bool voted)
+{
+	return ((sc->halt_check_type == type) &&
+	    (sc->halt_check_voted == voted));
+}
+
+static bool
+qcom_clk_branch2_wait_locked(struct qcom_clk_branch2_sc *sc, bool enable)
+{
+
+	if (qcom_clk_branch2_halt_check_type(sc,
+	    QCOM_CLK_BRANCH2_BRANCH_HALT_SKIP, false))
+		return (true);
+	if (qcom_clk_branch2_in_hwcg_mode_locked(sc))
+		return (true);
+
+	if ((qcom_clk_branch2_halt_check_type(sc,
+	      QCOM_CLK_BRANCH2_BRANCH_HALT_DELAY, false)) ||
+	    (enable == false && sc->halt_check_voted)) {
+		DELAY(10);
+		return (true);
+	}
+
+	if ((qcom_clk_branch2_halt_check_type(sc,
+	      QCOM_CLK_BRANCH2_BRANCH_HALT_INVERTED, false)) ||
+	    (qcom_clk_branch2_halt_check_type(sc,
+	      QCOM_CLK_BRANCH2_BRANCH_HALT, false)) ||
+	    (enable && sc->halt_check_voted)) {
+		int count;
+
+		for (count = 0; count < 200; count++) {
+			if (qcom_clk_branch2_check_halt_locked(sc, enable))
+				return (true);
+			DELAY(1);
+		}
+		DPRINTF(clknode_get_device(sc->clknode),
+		    "%s: enable stuck (%d)!\n", __func__, enable);
+		return (false);
+	}
+
+	/* Default */
+	return (true);
+}
+
+static int
+qcom_clk_branch2_set_gate(struct clknode *clk, bool enable)
+{
+	struct qcom_clk_branch2_sc *sc;
+	uint32_t reg;
+
+	sc = clknode_get_softc(clk);
+
+	DPRINTF(clknode_get_device(sc->clknode), "%s: called\n", __func__);
+
+	if (sc->enable_offset == 0) {
+		DPRINTF(clknode_get_device(sc->clknode),
+		    "%s: no enable_offset", __func__);
+		return (ENXIO);
+	}
+
+	DPRINTF(clknode_get_device(sc->clknode),
+	    "%s: called; enable=%d\n", __func__, enable);
+
+	CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
+	CLKDEV_READ_4(clknode_get_device(sc->clknode), sc->enable_offset,
+	    &reg);
+	if (enable) {
+		reg |= (1U << sc->enable_shift);
+	} else {
+		reg &= ~(1U << sc->enable_shift);
+	}
+	CLKDEV_WRITE_4(clknode_get_device(sc->clknode), sc->enable_offset,
+	    reg);
+
+	/*
+	 * Now wait for the clock branch to update!
+	 */
+	if (! qcom_clk_branch2_wait_locked(sc, enable)) {
+		CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
+		DPRINTF(clknode_get_device(sc->clknode),
+		    "%s: failed to wait!\n", __func__);
+		return (ENXIO);
+	}
+
+	CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
+
+	return (0);
+}
+
+static int
+qcom_clk_branch2_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+    int flags, int *stop)
+{
+	struct qcom_clk_branch2_sc *sc;
+
+	sc = clknode_get_softc(clk);
+
+	/* We only support what our parent clock is currently set as */
+	*fout = fin;
+
+	/* .. and stop here if we don't have SET_RATE_PARENT */
+	if (sc->flags & QCOM_CLK_BRANCH2_FLAGS_SET_RATE_PARENT)
+		*stop = 0;
+	else
+		*stop = 1;
+	return (0);
+}
+
+
+static clknode_method_t qcom_clk_branch2_methods[] = {
+	/* Device interface */
+	CLKNODEMETHOD(clknode_init,		qcom_clk_branch2_init),
+	CLKNODEMETHOD(clknode_set_gate,		qcom_clk_branch2_set_gate),
+	CLKNODEMETHOD(clknode_set_freq,		qcom_clk_branch2_set_freq),
+	CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(qcom_clk_branch2, qcom_clk_branch2_class,
+    qcom_clk_branch2_methods, sizeof(struct qcom_clk_branch2_sc),
+    clknode_class);
+
+int
+qcom_clk_branch2_register(struct clkdom *clkdom,
+    struct qcom_clk_branch2_def *clkdef)
+{
+	struct clknode *clk;
+	struct qcom_clk_branch2_sc *sc;
+
+	if (clkdef->flags & QCOM_CLK_BRANCH2_FLAGS_CRITICAL)
+		clkdef->clkdef.flags |= CLK_NODE_CANNOT_STOP;
+
+	clk = clknode_create(clkdom, &qcom_clk_branch2_class,
+	    &clkdef->clkdef);
+	if (clk == NULL)
+		return (1);
+
+	sc = clknode_get_softc(clk);
+	sc->clknode = clk;
+
+	sc->enable_offset = clkdef->enable_offset;
+	sc->enable_shift = clkdef->enable_shift;
+	sc->halt_reg = clkdef->halt_reg;
+	sc->hwcg_reg = clkdef->hwcg_reg;
+	sc->hwcg_bit = clkdef->hwcg_bit;
+	sc->halt_check_type = clkdef->halt_check_type;
+	sc->halt_check_voted = clkdef->halt_check_voted;
+	sc->flags = clkdef->flags;
+
+	clknode_register(clkdom, clk);
+
+	return (0);
+}
diff --git a/sys/dev/qcom_clk/qcom_clk_branch2.h b/sys/dev/qcom_clk/qcom_clk_branch2.h
new file mode 100644
index 000000000000..a3d868128ace
--- /dev/null
+++ b/sys/dev/qcom_clk/qcom_clk_branch2.h
@@ -0,0 +1,70 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	__QCOM_CLK_BRANCH2_H__
+#define	__QCOM_CLK_BRANCH2_H__
+
+#include "qcom_clk_freqtbl.h"
+
+/* halt is 1 */
+#define	QCOM_CLK_BRANCH2_BRANCH_HALT		0
+
+/* halt is inverted (ie, 0) */
+#define	QCOM_CLK_BRANCH2_BRANCH_HALT_INVERTED	1
+
+/* Don't check the bit, just delay */
+#define	QCOM_CLK_BRANCH2_BRANCH_HALT_DELAY	2
+
+/* Don't check the halt bit at all */
+#define	QCOM_CLK_BRANCH2_BRANCH_HALT_SKIP	3
+
+/* Flags */
+#define	QCOM_CLK_BRANCH2_FLAGS_CRITICAL		0x1
+#define	QCOM_CLK_BRANCH2_FLAGS_SET_RATE_PARENT	0x2
+
+struct qcom_clk_branch2_def {
+	struct clknode_init_def clkdef;
+
+	uint32_t flags;
+
+	uint32_t enable_offset;	/* enable register*/
+	uint32_t enable_shift;	/* enable bit shift */
+
+	uint32_t hwcg_reg;	/* hw clock gate register */
+	uint32_t hwcg_bit;
+	uint32_t halt_reg;	/* halt register */
+
+	uint32_t halt_check_type;
+	bool halt_check_voted;	/* whether to delay when waiting */
+};
+
+extern	int qcom_clk_branch2_register(struct clkdom *clkdom,
+	    struct qcom_clk_branch2_def *clkdef);
+
+#endif	/* __QCOM_CLK_BRANCH2_H__ */
diff --git a/sys/dev/qcom_clk/qcom_clk_branch2_reg.h b/sys/dev/qcom_clk/qcom_clk_branch2_reg.h
new file mode 100644
index 000000000000..0aff8d1cee12
--- /dev/null
+++ b/sys/dev/qcom_clk/qcom_clk_branch2_reg.h
@@ -0,0 +1,39 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	__QCOM_CLK_BRANCH2_REG_H__
+#define	__QCOM_CLK_BRANCH2_REG_H__
+
+#define	QCOM_CLK_BRANCH2_CLK_OFF			(1U << 31)
+#define	QCOM_CLK_BRANCH2_NOC_FSM_STATUS_SHIFT		28
+#define	QCOM_CLK_BRANCH2_NOC_FSM_STATUS_MASK		0x7
+#define	QCOM_CLK_BRANCH2_NOC_FSM_STATUS_ON	\
+	    (0x2 << QCOM_CLK_BRANCH2_NOC_FSM_STATUS_SHIFT)
+
+#endif	/* __QCOM_CLK_BRANCH2_REG_H__ */
diff --git a/sys/dev/qcom_clk/qcom_clk_fdiv.c b/sys/dev/qcom_clk/qcom_clk_fdiv.c
new file mode 100644
index 000000000000..e48ac790606a
--- /dev/null
+++ b/sys/dev/qcom_clk/qcom_clk_fdiv.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>.
+ *
+ * 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/lock.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_mux.h>
+
+#include "qcom_clk_fdiv.h"
+
+#include "clkdev_if.h"
+
+/*
+ * This is a fixed divisor node.  It represents some divisor
+ * that is setup by the boot environment and we don't have
+ * any need for the driver to go and fiddle with.
+ *
+ * It likely should just live in the extres/clk code.
+ */
+
+struct qcom_clk_fdiv_sc {
+	struct clknode	*clknode;
+	uint32_t divisor;
+};
+
+static int
+qcom_clk_fdiv_recalc(struct clknode *clk, uint64_t *freq)
+{
+	struct qcom_clk_fdiv_sc *sc;
+
+	sc = clknode_get_softc(clk);
+
+	if (freq == NULL || *freq == 0) {
+		printf("%s: called; NULL or 0 frequency\n", __func__);
+		return (ENXIO);
+	}
+
+	*freq = *freq / sc->divisor;
+	return (0);
+}
+
+static int
+qcom_clk_fdiv_init(struct clknode *clk, device_t dev)
+{
+	/*
+	 * There's only a single parent here for an fixed divisor,
+	 * so just set it to 0; the caller doesn't need to supply it.
+	 */
+	clknode_init_parent_idx(clk, 0);
+
+	return(0);
+}
+
+static clknode_method_t qcom_clk_fdiv_methods[] = {
+	/* Device interface */
+	CLKNODEMETHOD(clknode_init,		qcom_clk_fdiv_init),
+	CLKNODEMETHOD(clknode_recalc_freq,	qcom_clk_fdiv_recalc),
+	CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(qcom_clk_fepll, qcom_clk_fdiv_class, qcom_clk_fdiv_methods,
+   sizeof(struct qcom_clk_fdiv_sc), clknode_class);
+
+int
+qcom_clk_fdiv_register(struct clkdom *clkdom, struct qcom_clk_fdiv_def *clkdef)
+{
+	struct clknode *clk;
+	struct qcom_clk_fdiv_sc *sc;
+
+	clk = clknode_create(clkdom, &qcom_clk_fdiv_class, &clkdef->clkdef);
+	if (clk == NULL)
+		return (1);
+
+	sc = clknode_get_softc(clk);
+	sc->clknode = clk;
+
+	sc->divisor = clkdef->divisor;
+
+	clknode_register(clkdom, clk);
+
+	return (0);
+}
diff --git a/sys/dev/qcom_clk/qcom_clk_fdiv.h b/sys/dev/qcom_clk/qcom_clk_fdiv.h
new file mode 100644
index 000000000000..42c8dec07f6c
--- /dev/null
+++ b/sys/dev/qcom_clk/qcom_clk_fdiv.h
@@ -0,0 +1,41 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	__QCOM_CLK_FDIV_H__
+#define	__QCOM_CLK_FDIV_H__
+
+struct qcom_clk_fdiv_def {
+	struct clknode_init_def clkdef;
+	uint32_t divisor; /* Fixed divisor */
*** 1339 LINES SKIPPED ***