From nobody Mon Dec 27 21:05:19 2021 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 376611914E65; Mon, 27 Dec 2021 21:05:20 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4JN9DH6t56z4gl3; Mon, 27 Dec 2021 21:05:19 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id C74AB24C2E; Mon, 27 Dec 2021 21:05:19 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 1BRL5Joc084983; Mon, 27 Dec 2021 21:05:19 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 1BRL5JYC084982; Mon, 27 Dec 2021 21:05:19 GMT (envelope-from git) Date: Mon, 27 Dec 2021 21:05:19 GMT Message-Id: <202112272105.1BRL5JYC084982@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Adrian Chadd Subject: git: e34a491b3562 - main - qcom_clk: add the qualcomm clock nodes for the IPQ4018 List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: adrian X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: e34a491b35626b4209ef0a195e85a03a1089c572 Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1640639120; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=M3gonWFGi36PjFtnmE6UPJ1GBUk3bCT0k+zrB86e1bk=; b=cUUJgjqoz+PxROTGe/y5VIOhmp61BjMbxUfuq/JpVYIOxyHhzlo6B7yTYJfW5NYjNwdJLw /4lwLRl8ci4yT5prgUMRK+toKzn7OGWLvNK7UgisfMOZd+qyaDel8m3y426bI761OAkLu5 uUYInBzc9vbbR/qqWVSJRXrtU98U/vpTRof3tL/0oIo8Rd6n2tMajsgA5SeLW19654axig Cm4jT/wnC1bqY3O8LRh96GvpbvJFUzb8GW0chayuKlr+hPXaDcMUifB7miGcr7uJ3imNzf JzZ3GQ7u53s7qZ3Lf+sp1asZJ3hxjQgBY1AyNnUMb7ASc77T84PpZ4GIjXcj8A== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1640639120; a=rsa-sha256; cv=none; b=XtTI9+nSx70kPO/ndakKiXWgP6adc3CnsKOAfgk7d9u17KLGH128KvmLZBq4WAhCNPKwUz md/AEt9plV7hhyRlYcsNqRabNW+XBwpY0GCne11Bx2MjzZBK8Fe7N6O6mG0MwgsnncG6Ie +X9+CalGS4avEfQxdhx03DRBssHjRfSUX4AIAUENhs5a07S//fUkrqXTSVOZD35w7get01 S5HFQinIC/eq5amICVgURp/3S9aJpmqx/nSeIfb8SOmC5GdmgtlqrsbutAGAiE9Tcq3MbQ nipTmKr0jLbw32X4nlDssoJIn6EBX6VK7+az/kUrWGKRLXsQQwB+dbiiV6oYKw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=e34a491b35626b4209ef0a195e85a03a1089c572 commit e34a491b35626b4209ef0a195e85a03a1089c572 Author: Adrian Chadd AuthorDate: 2021-12-26 17:07:21 +0000 Commit: Adrian Chadd 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 . + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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, ®); + 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, + ®); + + 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, + ®); + 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 &= ~(((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 + * + * 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 . + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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, + ®); + + 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, + ®); + + 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, ®); + + 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, + ®); + 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 + * + * 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 + * + * 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 . + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 + * + * 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 ***