git: b12a863a1e14 - main - ipq4018: add initial reset driver support for the clock/reset controller.

From: Adrian Chadd <adrian_at_FreeBSD.org>
Date: Thu, 04 Nov 2021 16:02:56 UTC
The branch main has been updated by adrian:

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

commit b12a863a1e14610f6b145f235aa7452602038f9a
Author:     Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2021-10-31 03:43:27 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2021-11-04 16:02:41 +0000

    ipq4018: add initial reset driver support for the clock/reset controller.
    
    This implements the "reset controller" side of the clock/reset controller.
    It's a simple array of registers and bits to set.
    
    The register table itself comes from Linux; the rest of the code is a
    reimplementation.
    
    It doesn't yet implement or expose the clock side - I have a lot of
    reverse engineering to do before that!
    
    Reviewed by: andrew, manu, imp
    Differential Revision: https://reviews.freebsd.org/D32723
    
    Obtained from: Linux (registers)
---
 sys/arm/qualcomm/qcom_gcc_ipq4018.c       | 167 +++++++++++++++++++++++++++
 sys/arm/qualcomm/qcom_gcc_ipq4018_reset.c | 181 ++++++++++++++++++++++++++++++
 sys/arm/qualcomm/qcom_gcc_ipq4018_var.h   |  50 +++++++++
 sys/arm/qualcomm/std.ipq4018              |   3 +
 4 files changed, 401 insertions(+)

diff --git a/sys/arm/qualcomm/qcom_gcc_ipq4018.c b/sys/arm/qualcomm/qcom_gcc_ipq4018.c
new file mode 100644
index 000000000000..3002ae32a597
--- /dev/null
+++ b/sys/arm/qualcomm/qcom_gcc_ipq4018.c
@@ -0,0 +1,167 @@
+/*-
+ * 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 unmodified, 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 ``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 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.
+ */
+
+/* Driver for Qualcomm IPQ4018 clock and reset device */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sglist.h>
+#include <sys/random.h>
+#include <sys/stdatomic.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "hwreset_if.h"
+
+#include <dt-bindings/clock/qcom,gcc-ipq4019.h>
+
+#include <arm/qualcomm/qcom_gcc_ipq4018_var.h>
+
+
+static int	qcom_gcc_ipq4018_modevent(module_t, int, void *);
+
+static int	qcom_gcc_ipq4018_probe(device_t);
+static int	qcom_gcc_ipq4018_attach(device_t);
+static int	qcom_gcc_ipq4018_detach(device_t);
+
+static int
+qcom_gcc_ipq4018_modevent(module_t mod, int type, void *unused)
+{
+	int error;
+
+	switch (type) {
+	case MOD_LOAD:
+	case MOD_QUIESCE:
+	case MOD_UNLOAD:
+	case MOD_SHUTDOWN:
+		error = 0;
+		break;
+	default:
+		error = EOPNOTSUPP;
+		break;
+	}
+
+	return (error);
+}
+
+static int
+qcom_gcc_ipq4018_probe(device_t dev)
+{
+	if (! ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_is_compatible(dev, "qcom,gcc-ipq4019") == 0)
+		return (ENXIO);
+
+	return (0);
+}
+
+static int
+qcom_gcc_ipq4018_attach(device_t dev)
+{
+	struct qcom_gcc_ipq4018_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	/* Found a compatible device! */
+	sc->dev = dev;
+
+	sc->reg_rid = 0;
+	sc->reg = bus_alloc_resource_anywhere(dev, SYS_RES_MEMORY,
+	    &sc->reg_rid, 0x60000, RF_ACTIVE);
+	if (sc->reg == NULL) {
+		device_printf(dev, "Couldn't allocate memory resource!\n");
+		return (ENXIO);
+	}
+
+	device_set_desc(dev, "Qualcomm IPQ4018 Clock/Reset Controller");
+
+	mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+	/*
+	 * Register as a reset provider.
+	 */
+	hwreset_register_ofw_provider(dev);
+
+	return (0);
+}
+
+static int
+qcom_gcc_ipq4018_detach(device_t dev)
+{
+	struct qcom_gcc_ipq4018_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (sc->reg != NULL) {
+		bus_release_resource(sc->dev, SYS_RES_MEMORY,
+		    sc->reg_rid, sc->reg);
+	}
+	return (0);
+}
+
+static device_method_t qcom_gcc_ipq4018_methods[] = {
+	/* Device methods. */
+	DEVMETHOD(device_probe,		qcom_gcc_ipq4018_probe),
+	DEVMETHOD(device_attach,	qcom_gcc_ipq4018_attach),
+	DEVMETHOD(device_detach,	qcom_gcc_ipq4018_detach),
+
+	/* Reset interface */
+	DEVMETHOD(hwreset_assert,	qcom_gcc_ipq4018_hwreset_assert),
+	DEVMETHOD(hwreset_is_asserted,	qcom_gcc_ipq4018_hwreset_is_asserted),
+
+	DEVMETHOD_END
+};
+
+static driver_t qcom_gcc_ipq4018_driver = {
+	"qcom_gcc",
+	qcom_gcc_ipq4018_methods,
+	sizeof(struct qcom_gcc_ipq4018_softc)
+};
+static devclass_t qcom_gcc_ipq4018_devclass;
+
+EARLY_DRIVER_MODULE(qcom_gcc_ipq4018, simplebus, qcom_gcc_ipq4018_driver,
+    qcom_gcc_ipq4018_devclass, qcom_gcc_ipq4018_modevent, 0,
+    BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
+EARLY_DRIVER_MODULE(qcom_gcc_ipq4018, ofwbus, qcom_gcc_ipq4018_driver,
+    qcom_gcc_ipq4018_devclass, qcom_gcc_ipq4018_modevent, 0,
+    BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(qcom_gcc_ipq4018_random, 1);
diff --git a/sys/arm/qualcomm/qcom_gcc_ipq4018_reset.c b/sys/arm/qualcomm/qcom_gcc_ipq4018_reset.c
new file mode 100644
index 000000000000..754e7636ff6e
--- /dev/null
+++ b/sys/arm/qualcomm/qcom_gcc_ipq4018_reset.c
@@ -0,0 +1,181 @@
+/*-
+ * 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 unmodified, 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 ``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 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.
+ */
+
+/* Driver for Qualcomm IPQ4018 clock and reset device */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sglist.h>
+#include <sys/random.h>
+#include <sys/stdatomic.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/hwreset/hwreset.h>
+
+#include "hwreset_if.h"
+
+#include <dt-bindings/clock/qcom,gcc-ipq4019.h>
+
+#include <arm/qualcomm/qcom_gcc_ipq4018_var.h>
+
+
+static const struct qcom_gcc_ipq4018_reset_entry gcc_ipq4019_reset_list[] = {
+	[WIFI0_CPU_INIT_RESET] = { 0x1f008, 5 },
+	[WIFI0_RADIO_SRIF_RESET] = { 0x1f008, 4 },
+	[WIFI0_RADIO_WARM_RESET] = { 0x1f008, 3 },
+	[WIFI0_RADIO_COLD_RESET] = { 0x1f008, 2 },
+	[WIFI0_CORE_WARM_RESET] = { 0x1f008, 1 },
+	[WIFI0_CORE_COLD_RESET] = { 0x1f008, 0 },
+	[WIFI1_CPU_INIT_RESET] = { 0x20008, 5 },
+	[WIFI1_RADIO_SRIF_RESET] = { 0x20008, 4 },
+	[WIFI1_RADIO_WARM_RESET] = { 0x20008, 3 },
+	[WIFI1_RADIO_COLD_RESET] = { 0x20008, 2 },
+	[WIFI1_CORE_WARM_RESET] = { 0x20008, 1 },
+	[WIFI1_CORE_COLD_RESET] = { 0x20008, 0 },
+	[USB3_UNIPHY_PHY_ARES] = { 0x1e038, 5 },
+	[USB3_HSPHY_POR_ARES] = { 0x1e038, 4 },
+	[USB3_HSPHY_S_ARES] = { 0x1e038, 2 },
+	[USB2_HSPHY_POR_ARES] = { 0x1e01c, 4 },
+	[USB2_HSPHY_S_ARES] = { 0x1e01c, 2 },
+	[PCIE_PHY_AHB_ARES] = { 0x1d010, 11 },
+	[PCIE_AHB_ARES] = { 0x1d010, 10 },
+	[PCIE_PWR_ARES] = { 0x1d010, 9 },
+	[PCIE_PIPE_STICKY_ARES] = { 0x1d010, 8 },
+	[PCIE_AXI_M_STICKY_ARES] = { 0x1d010, 7 },
+	[PCIE_PHY_ARES] = { 0x1d010, 6 },
+	[PCIE_PARF_XPU_ARES] = { 0x1d010, 5 },
+	[PCIE_AXI_S_XPU_ARES] = { 0x1d010, 4 },
+	[PCIE_AXI_M_VMIDMT_ARES] = { 0x1d010, 3 },
+	[PCIE_PIPE_ARES] = { 0x1d010, 2 },
+	[PCIE_AXI_S_ARES] = { 0x1d010, 1 },
+	[PCIE_AXI_M_ARES] = { 0x1d010, 0 },
+	[ESS_RESET] = { 0x12008, 0},
+	[GCC_BLSP1_BCR] = {0x01000, 0},
+	[GCC_BLSP1_QUP1_BCR] = {0x02000, 0},
+	[GCC_BLSP1_UART1_BCR] = {0x02038, 0},
+	[GCC_BLSP1_QUP2_BCR] = {0x03008, 0},
+	[GCC_BLSP1_UART2_BCR] = {0x03028, 0},
+	[GCC_BIMC_BCR] = {0x04000, 0},
+	[GCC_TLMM_BCR] = {0x05000, 0},
+	[GCC_IMEM_BCR] = {0x0E000, 0},
+	[GCC_ESS_BCR] = {0x12008, 0},
+	[GCC_PRNG_BCR] = {0x13000, 0},
+	[GCC_BOOT_ROM_BCR] = {0x13008, 0},
+	[GCC_CRYPTO_BCR] = {0x16000, 0},
+	[GCC_SDCC1_BCR] = {0x18000, 0},
+	[GCC_SEC_CTRL_BCR] = {0x1A000, 0},
+	[GCC_AUDIO_BCR] = {0x1B008, 0},
+	[GCC_QPIC_BCR] = {0x1C000, 0},
+	[GCC_PCIE_BCR] = {0x1D000, 0},
+	[GCC_USB2_BCR] = {0x1E008, 0},
+	[GCC_USB2_PHY_BCR] = {0x1E018, 0},
+	[GCC_USB3_BCR] = {0x1E024, 0},
+	[GCC_USB3_PHY_BCR] = {0x1E034, 0},
+	[GCC_SYSTEM_NOC_BCR] = {0x21000, 0},
+	[GCC_PCNOC_BCR] = {0x2102C, 0},
+	[GCC_DCD_BCR] = {0x21038, 0},
+	[GCC_SNOC_BUS_TIMEOUT0_BCR] = {0x21064, 0},
+	[GCC_SNOC_BUS_TIMEOUT1_BCR] = {0x2106C, 0},
+	[GCC_SNOC_BUS_TIMEOUT2_BCR] = {0x21074, 0},
+	[GCC_SNOC_BUS_TIMEOUT3_BCR] = {0x2107C, 0},
+	[GCC_PCNOC_BUS_TIMEOUT0_BCR] = {0x21084, 0},
+	[GCC_PCNOC_BUS_TIMEOUT1_BCR] = {0x2108C, 0},
+	[GCC_PCNOC_BUS_TIMEOUT2_BCR] = {0x21094, 0},
+	[GCC_PCNOC_BUS_TIMEOUT3_BCR] = {0x2109C, 0},
+	[GCC_PCNOC_BUS_TIMEOUT4_BCR] = {0x210A4, 0},
+	[GCC_PCNOC_BUS_TIMEOUT5_BCR] = {0x210AC, 0},
+	[GCC_PCNOC_BUS_TIMEOUT6_BCR] = {0x210B4, 0},
+	[GCC_PCNOC_BUS_TIMEOUT7_BCR] = {0x210BC, 0},
+	[GCC_PCNOC_BUS_TIMEOUT8_BCR] = {0x210C4, 0},
+	[GCC_PCNOC_BUS_TIMEOUT9_BCR] = {0x210CC, 0},
+	[GCC_TCSR_BCR] = {0x22000, 0},
+	[GCC_MPM_BCR] = {0x24000, 0},
+	[GCC_SPDM_BCR] = {0x25000, 0},
+};
+
+int
+qcom_gcc_ipq4018_hwreset_assert(device_t dev, intptr_t id, bool reset)
+{
+	struct qcom_gcc_ipq4018_softc *sc;
+	uint32_t reg;
+
+	sc = device_get_softc(dev);
+
+	if (id > nitems(gcc_ipq4019_reset_list)) {
+		device_printf(dev, "%s: invalid id (%d)\n", __func__, id);
+		return (EINVAL);
+	}
+
+	device_printf(dev, "%s: called; id=%d, reset=%d\n", __func__, id, reset);
+	mtx_lock(&sc->mtx);
+	reg = bus_read_4(sc->reg, gcc_ipq4019_reset_list[id].reg);
+	if (reset)
+		reg |= (1U << gcc_ipq4019_reset_list[id].bit);
+	else
+		reg &= ~(1U << gcc_ipq4019_reset_list[id].bit);
+	bus_write_4(sc->reg, gcc_ipq4019_reset_list[id].reg, reg);
+	mtx_unlock(&sc->mtx);
+	return (0);
+}
+
+int
+qcom_gcc_ipq4018_hwreset_is_asserted(device_t dev, intptr_t id, bool *reset)
+{
+	struct qcom_gcc_ipq4018_softc *sc;
+	uint32_t reg;
+
+	sc = device_get_softc(dev);
+
+	if (id > nitems(gcc_ipq4019_reset_list)) {
+		device_printf(dev, "%s: invalid id (%d)\n", __func__, id);
+		return (EINVAL);
+	}
+	mtx_lock(&sc->mtx);
+	reg = bus_read_4(sc->reg, gcc_ipq4019_reset_list[id].reg);
+	if (reg & ((1U << gcc_ipq4019_reset_list[id].bit)))
+		*reset = true;
+	else
+		*reset = false;
+	mtx_unlock(&sc->mtx);
+
+	device_printf(dev, "called; id=%d\n", id);
+	return (0);
+}
+
diff --git a/sys/arm/qualcomm/qcom_gcc_ipq4018_var.h b/sys/arm/qualcomm/qcom_gcc_ipq4018_var.h
new file mode 100644
index 000000000000..3997e1860e43
--- /dev/null
+++ b/sys/arm/qualcomm/qcom_gcc_ipq4018_var.h
@@ -0,0 +1,50 @@
+/*-
+ * 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_GCC_IPQ4018_VAR_H__
+#define	__QCOM_GCC_IPQ4018_VAR_H__
+
+struct qcom_gcc_ipq4018_reset_entry {
+	uint32_t	reg;
+	uint32_t	bit;
+};
+
+struct qcom_gcc_ipq4018_softc {
+	device_t		dev;
+	int			reg_rid;
+	struct resource		*reg;
+	struct mtx		mtx;
+};
+
+extern	int qcom_gcc_ipq4018_hwreset_assert(device_t dev, intptr_t id,
+	    bool reset);
+extern	int qcom_gcc_ipq4018_hwreset_is_asserted(device_t dev, intptr_t id,
+	    bool *reset);
+
+#endif	/* __QCOM_GCC_IPQ4018_VAR_H__ */
diff --git a/sys/arm/qualcomm/std.ipq4018 b/sys/arm/qualcomm/std.ipq4018
index 099fd81b5171..7e8ac39e7222 100644
--- a/sys/arm/qualcomm/std.ipq4018
+++ b/sys/arm/qualcomm/std.ipq4018
@@ -4,3 +4,6 @@ arm/qualcomm/qcom_scm_legacy.c		standard
 arm/qualcomm/qcom_cpu_kpssv2.c		optional smp
 
 dev/qcom_rnd/qcom_rnd.c			optional qcom_rnd
+arm/qualcomm/qcom_gcc_ipq4018.c		optional qcom_gcc_ipq4018
+arm/qualcomm/qcom_gcc_ipq4018_reset.c	optional qcom_gcc_ipq4018
+