Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144369938
D32723.id97903.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
47 KB
Referenced Files
None
Subscribers
None
D32723.id97903.diff
View Options
Index: sys/arm/conf/ASUS_AC1300
===================================================================
--- sys/arm/conf/ASUS_AC1300
+++ sys/arm/conf/ASUS_AC1300
@@ -38,8 +38,7 @@
options LINUX_BOOT_ABI
options SCHED_ULE
-# DEFINITELY not ready for SMP yet!
-# options SMP
+options SMP
options PLATFORM
device loop
@@ -50,3 +49,4 @@
device ether
device mii
device bpf
+
Index: sys/arm/conf/std.qca
===================================================================
--- sys/arm/conf/std.qca
+++ sys/arm/conf/std.qca
@@ -12,6 +12,18 @@
device uart
device uart_msm # Qualcomm MSM UART driver
+# EXT_RESOURCES pseudo devices
+options EXT_RESOURCES
+device clk
+device phy
+device hwreset
+device nvmem
+device regulator
+device syscon
+
+# Random
+device qcom_rnd
+
device gic
# MMC/SD/SDIO Card slot support
@@ -22,6 +34,12 @@
device generic_timer
device mpcore_timer
+# PSCI - SMC calls, needed for qualcomm SCM
+device psci
+
+# Clock/Reset provider
+device qcom_gcc_ipq4018
+
options FDT
# Disable CP14 work in DDB as TZ won't let us by default
Index: sys/arm/qualcomm/ipq4018_machdep.c
===================================================================
--- sys/arm/qualcomm/ipq4018_machdep.c
+++ sys/arm/qualcomm/ipq4018_machdep.c
@@ -36,10 +36,12 @@
#include <sys/reboot.h>
#include <sys/devmap.h>
#include <sys/physmem.h>
+#include <sys/lock.h>
#include <vm/vm.h>
#include <machine/bus.h>
+#include <machine/fdt.h>
#include <machine/intr.h>
#include <machine/machdep.h>
#include <machine/platformvar.h>
@@ -94,12 +96,52 @@
* a call to pmap_mapdev() when the bus space code is doing its thing.
*/
devmap_add_entry(IPQ4018_MEM_UART1_START, IPQ4018_MEM_UART1_SIZE);
+
+ /*
+ * This covers a bunch of the reset block, which includes the PS-HOLD
+ * register for dropping power.
+ */
+ devmap_add_entry(IPQ4018_MEM_PSHOLD_START, IPQ4018_MEM_PSHOLD_SIZE);
+
return (0);
}
+/*
+ * This toggles the PS-HOLD register which on most IPQ devices will toggle
+ * the power control block and reset the SoC.
+ *
+ * However, there are apparently some units out there where this is not
+ * appropriate and instead the watchdog needs to be used.
+ *
+ * For now since there's only going to be one or two initial supported boards
+ * this will be fine. But if this doesn't reboot cleanly, now you know.
+ */
+static void
+ipq4018_cpu_reset_pshold(void)
+{
+ bus_space_handle_t pshold;
+
+ printf("%s: called\n", __func__);
+
+ bus_space_map(fdtbus_bs_tag, IPQ4018_MEM_PSHOLD_START,
+ IPQ4018_MEM_PSHOLD_SIZE, 0, &pshold);
+ bus_space_write_4(fdtbus_bs_tag, pshold, 0, 0);
+ bus_space_barrier(fdtbus_bs_tag, pshold, 0, 0x4,
+ BUS_SPACE_BARRIER_WRITE);
+}
+
static void
ipq4018_cpu_reset(platform_t plat)
{
+ spinlock_enter();
+ dsb();
+
+ ipq4018_cpu_reset_pshold();
+
+ /* Spin */
+ printf("%s: spinning\n", __func__);
+ while(1)
+ ;
}
/*
Index: sys/arm/qualcomm/ipq4018_mp.c
===================================================================
--- sys/arm/qualcomm/ipq4018_mp.c
+++ sys/arm/qualcomm/ipq4018_mp.c
@@ -35,27 +35,82 @@
#include <sys/bus.h>
#include <sys/reboot.h>
#include <sys/devmap.h>
+#include <sys/smp.h>
#include <vm/vm.h>
+#include <machine/cpu.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/machdep.h>
#include <machine/platformvar.h>
+#include <machine/smp.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_cpu.h>
#include <arm/qualcomm/ipq4018_machdep.h>
+#include <arm/qualcomm/qcom_scm_legacy.h>
+#include <arm/qualcomm/qcom_cpu_kpssv2.h>
#include "platform_if.h"
void
ipq4018_mp_setmaxid(platform_t plat)
{
+ int ncpu;
+
+ /* If we've already set the global vars don't bother to do it again. */
+ if (mp_ncpus != 0)
+ return;
+
+ /* Read current CP15 Cache Size ID Register */
+ ncpu = cp15_l2ctlr_get();
+ ncpu = CPUV7_L2CTLR_NPROC(ncpu);
+
+ mp_ncpus = ncpu;
+ mp_maxid = ncpu - 1;
+
+ printf("SMP: ncpu=%d\n", ncpu);
+}
+
+static boolean_t
+ipq4018_start_ap(u_int id, phandle_t node, u_int addr_cells, pcell_t *arg)
+{
+
+ /*
+ * For the IPQ401x we assume the enable method is
+ * "qcom,kpss-acc-v2". If this path gets turned into
+ * something more generic for other 32 bit qualcomm
+ * SoCs then we'll likely want to turn this into a
+ * switch based on "enable-method".
+ */
+ return qcom_cpu_kpssv2_regulator_start(id, node);
}
void
ipq4018_mp_start_ap(platform_t plat)
{
+ int ret;
+
+ /*
+ * First step - SCM call to set the cold boot address to mpentry, so
+ * CPUs hopefully start in the MP path.
+ */
+ ret = qcom_scm_legacy_mp_set_cold_boot_address((vm_offset_t) mpentry);
+ if (ret != 0)
+ panic("%s: Couldn't set cold boot address via SCM "
+ "(error 0x%08x)", __func__, ret);
+
+ /*
+ * Next step - loop over the CPU nodes and do the per-CPU setup
+ * required to power on the CPUs themselves.
+ */
+ ofw_cpu_early_foreach(ipq4018_start_ap, true);
+
+ /*
+ * The next set of IPIs to the CPUs will wake them up and enter
+ * mpentry.
+ */
}
Index: sys/arm/qualcomm/ipq4018_reg.h
===================================================================
--- sys/arm/qualcomm/ipq4018_reg.h
+++ sys/arm/qualcomm/ipq4018_reg.h
@@ -39,4 +39,7 @@
#define IPQ4018_MEM_UART1_START 0x078af000
#define IPQ4018_MEM_UART1_SIZE 0x00001000
+#define IPQ4018_MEM_PSHOLD_START 0x004ab000
+#define IPQ4018_MEM_PSHOLD_SIZE 0x00001000
+
#endif
Index: sys/arm/qualcomm/qcom_cpu_kpssv2.h
===================================================================
--- sys/arm/qualcomm/qcom_cpu_kpssv2.h
+++ sys/arm/qualcomm/qcom_cpu_kpssv2.h
@@ -27,16 +27,9 @@
* $FreeBSD$
*/
-#ifndef IPQ4018_REG_H
-#define IPQ4018_REG_H
+#ifndef __QCOM_CPU_KPSSV2_H__
+#define __QCOM_CPU_KPSSV2_H__
-#define IPQ4018_MEM_SMEM_START 0x87e00000
-#define IPQ4018_MEM_SMEM_SIZE 0x00080000
+extern boolean_t qcom_cpu_kpssv2_regulator_start(u_int id, phandle_t node);
-#define IPQ4018_MEM_TZ_START 0x87e80000
-#define IPQ4018_MEM_TZ_SIZE 0x00180000
-
-#define IPQ4018_MEM_UART1_START 0x078af000
-#define IPQ4018_MEM_UART1_SIZE 0x00001000
-
-#endif
+#endif /* __QCOM_CPU_KPSSV2_H__ */
Index: sys/arm/qualcomm/qcom_cpu_kpssv2.c
===================================================================
--- /dev/null
+++ sys/arm/qualcomm/qcom_cpu_kpssv2.c
@@ -0,0 +1,211 @@
+/*-
+ * 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.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/reboot.h>
+#include <sys/devmap.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+
+#include <machine/cpu.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/machdep.h>
+#include <machine/platformvar.h>
+#include <machine/smp.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_cpu.h>
+
+#include <arm/qualcomm/qcom_cpu_kpssv2_reg.h>
+#include <arm/qualcomm/qcom_cpu_kpssv2.h>
+
+#include "platform_if.h"
+
+/*
+ * Since DELAY() hangs this early, we need some way to
+ * delay things to settle.
+ */
+static inline void
+loop_delay(int usec)
+{
+ int lcount = usec * 100000;
+
+ for (volatile int i = 0; i < lcount; i++)
+ ;
+}
+
+/*
+ * This is the KPSSv2 (eg IPQ4018) regulator path for CPU
+ * and shared L2 cache power-on.
+ */
+boolean_t
+qcom_cpu_kpssv2_regulator_start(u_int id, phandle_t node)
+{
+ phandle_t acc_phandle, l2_phandle, saw_phandle;
+ bus_space_tag_t acc_tag, saw_tag;
+ bus_space_handle_t acc_handle, saw_handle;
+ bus_size_t acc_sz, saw_sz;
+ ssize_t sret;
+ int ret;
+ uint32_t reg_val;
+
+ /*
+ * We don't need to power up CPU 0! This will power it
+ * down first and ... then everything hangs.
+ */
+ if (id == 0)
+ return true;
+
+ /*
+ * Walk the qcom,acc and next-level-cache entries to find their
+ * child phandles and thus regulators.
+ *
+ * The qcom,acc is a phandle to a node.
+ *
+ * The next-level-cache actually is a phandle through to a qcom,saw
+ * entry.
+ */
+ sret = OF_getencprop(node, "qcom,acc", (void *) &acc_phandle,
+ sizeof(acc_phandle));
+ if (sret != sizeof(acc_phandle))
+ panic("***couldn't get phandle for qcom,acc");
+ acc_phandle = OF_node_from_xref(acc_phandle);
+
+ sret = OF_getencprop(node, "next-level-cache", (void *) &l2_phandle,
+ sizeof(l2_phandle));
+ if (sret != sizeof(l2_phandle))
+ panic("***couldn't get phandle for next-level-cache");
+ l2_phandle = OF_node_from_xref(l2_phandle);
+
+ sret = OF_getencprop(l2_phandle, "qcom,saw", (void *) &saw_phandle,
+ sizeof(saw_phandle));
+ if (sret != sizeof(saw_phandle))
+ panic("***couldn't get phandle for qcom,saw");
+ l2_phandle = OF_node_from_xref(l2_phandle);
+
+ /*
+ * Now that we have the phandles referencing the correct locations,
+ * do some KVA mappings so we can go access the registers.
+ */
+ ret = OF_decode_addr(acc_phandle, 0, &acc_tag, &acc_handle, &acc_sz);
+ if (ret != 0)
+ panic("*** couldn't map qcom,acc space (%d)", ret);
+ ret = OF_decode_addr(saw_phandle, 0, &saw_tag, &saw_handle, &saw_sz);
+ if (ret != 0)
+ panic("*** couldn't map next-level-cache -> "
+ "qcom,saw space (%d)", ret);
+
+ /*
+ * Power sequencing to ensure the cores are off, then power them on
+ * and bring them out of reset.
+ */
+
+ /*
+ * BHS: off
+ * LDO: bypassed, powered off
+ */
+ reg_val = (64 << QCOM_APC_PWR_GATE_CTL_BHS_CNT_SHIFT)
+ | (0x3f << QCOM_APC_PWR_GATE_CTL_LDO_PWR_DWN_SHIFT)
+ | QCOM_APC_PWR_GATE_CTL_BHS_EN;
+ bus_space_write_4(acc_tag, acc_handle, QCOM_APC_PWR_GATE_CTL, reg_val);
+ mb();
+ /* Settle time */
+ loop_delay(1);
+
+ /*
+ * Start up BHS segments.
+ */
+ reg_val |= 0x3f << QCOM_APC_PWR_GATE_CTL_BHS_SEG_SHIFT;
+ bus_space_write_4(acc_tag, acc_handle, QCOM_APC_PWR_GATE_CTL, reg_val);
+ mb();
+ /* Settle time */
+ loop_delay(1);
+
+ /*
+ * Switch on the LDO bypass; BHS will now supply power.
+ */
+ reg_val |= 0x3f << QCOM_APC_PWR_GATE_CTL_LDO_BYP_SHIFT;
+ bus_space_write_4(acc_tag, acc_handle, QCOM_APC_PWR_GATE_CTL, reg_val);
+
+ /*
+ * Shared L2 regulator control.
+ */
+ bus_space_write_4(saw_tag, saw_handle, QCOM_APCS_SAW2_2_VCTL, 0x10003);
+ mb();
+ /* Settle time */
+ loop_delay(50);
+
+ /*
+ * Put the core in reset.
+ */
+ reg_val = QCOM_APCS_CPU_PWR_CTL_COREPOR_RST
+ | QCOM_APCS_CPU_PWR_CTL_CLAMP;
+ bus_space_write_4(acc_tag, acc_handle, QCOM_APCS_CPU_PWR_CTL, reg_val);
+ mb();
+ loop_delay(2);
+
+ /*
+ * Remove power-down clamp.
+ */
+ reg_val &= ~QCOM_APCS_CPU_PWR_CTL_CLAMP;
+ bus_space_write_4(acc_tag, acc_handle, QCOM_APCS_CPU_PWR_CTL, reg_val);
+ mb();
+ loop_delay(2);
+
+ /*
+ * Clear core power reset.
+ */
+ reg_val &= ~QCOM_APCS_CPU_PWR_CTL_COREPOR_RST;
+ bus_space_write_4(acc_tag, acc_handle, QCOM_APCS_CPU_PWR_CTL, reg_val);
+ mb();
+
+ /*
+ * The power is ready, the core is out of reset, signal the core
+ * to power up.
+ */
+ reg_val |= QCOM_APCS_CPU_PWR_CTL_CORE_PWRD_UP;
+ bus_space_write_4(acc_tag, acc_handle, QCOM_APCS_CPU_PWR_CTL, reg_val);
+ mb();
+
+ /*
+ * Finished with these KVA mappings, so release them.
+ */
+ bus_space_unmap(acc_tag, acc_handle, acc_sz);
+ bus_space_unmap(saw_tag, saw_handle, saw_sz);
+
+ return true;
+}
Index: sys/arm/qualcomm/qcom_cpu_kpssv2_reg.h
===================================================================
--- sys/arm/qualcomm/qcom_cpu_kpssv2_reg.h
+++ sys/arm/qualcomm/qcom_cpu_kpssv2_reg.h
@@ -27,16 +27,32 @@
* $FreeBSD$
*/
-#ifndef IPQ4018_REG_H
-#define IPQ4018_REG_H
+#ifndef __QCOM_CPU_KPSSV2_REG_H__
+#define __QCOM_CPU_KPSSV2_REG_H__
-#define IPQ4018_MEM_SMEM_START 0x87e00000
-#define IPQ4018_MEM_SMEM_SIZE 0x00080000
-#define IPQ4018_MEM_TZ_START 0x87e80000
-#define IPQ4018_MEM_TZ_SIZE 0x00180000
+/*
+ * APCS CPU core regulator registers.
+ */
+#define QCOM_APCS_CPU_PWR_CTL 0x04
+#define QCOM_APCS_CPU_PWR_CTL_PLL_CLAMP (1U << 8)
+#define QCOM_APCS_CPU_PWR_CTL_CORE_PWRD_UP (1U << 7)
+#define QCOM_APCS_CPU_PWR_CTL_COREPOR_RST (1U << 5)
+#define QCOM_APCS_CPU_PWR_CTL_CORE_RST (1U << 4)
+#define QCOM_APCS_CPU_PWR_CTL_L2DT_SLP (1U << 3)
+#define QCOM_APCS_CPU_PWR_CTL_CLAMP (1U << 0)
+
+#define QCOM_APC_PWR_GATE_CTL 0x14
+#define QCOM_APC_PWR_GATE_CTL_BHS_CNT_SHIFT 24
+#define QCOM_APC_PWR_GATE_CTL_LDO_PWR_DWN_SHIFT 16
+#define QCOM_APC_PWR_GATE_CTL_LDO_BYP_SHIFT 8
+#define QCOM_APC_PWR_GATE_CTL_BHS_SEG_SHIFT 1
+#define QCOM_APC_PWR_GATE_CTL_BHS_EN (1U << 0)
-#define IPQ4018_MEM_UART1_START 0x078af000
-#define IPQ4018_MEM_UART1_SIZE 0x00001000
-#endif
+/*
+ * L2 cache regulator registers.
+ */
+#define QCOM_APCS_SAW2_2_VCTL 0x1c
+
+#endif /* __QCOM_CPU_KPSSV2_REG_H__ */
Index: sys/arm/qualcomm/qcom_gcc_ipq4018.c
===================================================================
--- /dev/null
+++ 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);
Index: sys/arm/qualcomm/qcom_gcc_ipq4018_reset.c
===================================================================
--- /dev/null
+++ 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);
+}
+
Index: sys/arm/qualcomm/qcom_gcc_ipq4018_var.h
===================================================================
--- sys/arm/qualcomm/qcom_gcc_ipq4018_var.h
+++ sys/arm/qualcomm/qcom_gcc_ipq4018_var.h
@@ -27,16 +27,24 @@
* $FreeBSD$
*/
-#ifndef IPQ4018_REG_H
-#define IPQ4018_REG_H
+#ifndef __QCOM_GCC_IPQ4018_VAR_H__
+#define __QCOM_GCC_IPQ4018_VAR_H__
-#define IPQ4018_MEM_SMEM_START 0x87e00000
-#define IPQ4018_MEM_SMEM_SIZE 0x00080000
+struct qcom_gcc_ipq4018_reset_entry {
+ uint32_t reg;
+ uint32_t bit;
+};
-#define IPQ4018_MEM_TZ_START 0x87e80000
-#define IPQ4018_MEM_TZ_SIZE 0x00180000
+struct qcom_gcc_ipq4018_softc {
+ device_t dev;
+ int reg_rid;
+ struct resource *reg;
+ struct mtx mtx;
+};
-#define IPQ4018_MEM_UART1_START 0x078af000
-#define IPQ4018_MEM_UART1_SIZE 0x00001000
+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
+#endif /* __QCOM_GCC_IPQ4018_VAR_H__ */
Index: sys/arm/qualcomm/qcom_scm_defs.h
===================================================================
--- /dev/null
+++ sys/arm/qualcomm/qcom_scm_defs.h
@@ -0,0 +1,122 @@
+/*-
+ * 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_SCM_DEFS_H__
+#define __QCOM_SCM_DEFS_H__
+
+/*
+ * Maximum SCM arguments and return values.
+ */
+#define MAX_QCOM_SCM_ARGS 10
+#define MAX_QCOM_SCM_RETS 3
+
+/*
+ * SCM argument type definitions.
+ */
+#define QCOM_SCM_ARGTYPE_VAL 0x00
+#define QCOM_SCM_ARGTYPE_RO 0x01
+#define QCOM_SCM_ARGTYPE_RW 0x02
+#define QCOM_SCM_ARGTYPE_BUFVAL 0x03
+
+/*
+ * SCM calls + arguments.
+ */
+#define QCOM_SCM_SVC_BOOT 0x01
+#define QCOM_SCM_BOOT_SET_ADDR 0x01
+#define QCOM_SCM_BOOT_TERMINATE_PC 0x02
+#define QCOM_SCM_BOOT_SET_DLOAD_MODE 0x10
+#define QCOM_SCM_BOOT_SET_REMOTE_STATE 0x0a
+#define QCOM_SCM_FLUSH_FLAG_MASK 0x3
+
+/* Flags for QCOM_SCM_BOOT_SET_ADDR argv[0] */
+/* Note: no COLDBOOT for CPU0, it's already booted */
+#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01
+#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02
+#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04
+#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08
+#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10
+#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20
+#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40
+
+#define QCOM_SCM_SVC_PIL 0x02
+#define QCOM_SCM_PIL_PAS_INIT_IMAGE 0x01
+#define QCOM_SCM_PIL_PAS_MEM_SETUP 0x02
+#define QCOM_SCM_PIL_PAS_AUTH_AND_RESET 0x05
+#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
+#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
+#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
+
+#define QCOM_SCM_SVC_IO 0x05
+#define QCOM_SCM_IO_READ 0x01
+#define QCOM_SCM_IO_WRITE 0x02
+
+/*
+ * Fetch SCM call availability information.
+ */
+#define QCOM_SCM_SVC_INFO 0x06
+#define QCOM_SCM_INFO_IS_CALL_AVAIL 0x01
+
+#define QCOM_SCM_SVC_MP 0x0c
+#define QCOM_SCM_MP_RESTORE_SEC_CFG 0x02
+#define QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE 0x03
+#define QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT 0x04
+#define QCOM_SCM_MP_VIDEO_VAR 0x08
+#define QCOM_SCM_MP_ASSIGN 0x16
+
+#define QCOM_SCM_SVC_OCMEM 0x0f
+#define QCOM_SCM_OCMEM_LOCK_CMD 0x01
+#define QCOM_SCM_OCMEM_UNLOCK_CMD 0x02
+
+#define QCOM_SCM_SVC_ES 0x10
+#define QCOM_SCM_ES_INVALIDATE_ICE_KEY 0x03
+#define QCOM_SCM_ES_CONFIG_SET_ICE_KEY 0x04
+
+#define QCOM_SCM_SVC_HDCP 0x11
+#define QCOM_SCM_HDCP_INVOKE 0x01
+
+#define QCOM_SCM_SVC_LMH 0x13
+#define QCOM_SCM_LMH_LIMIT_PROFILE_CHANGE 0x01
+#define QCOM_SCM_LMH_LIMIT_DCVSH 0x10
+
+#define QCOM_SCM_SVC_SMMU_PROGRAM 0x15
+#define QCOM_SCM_SMMU_CONFIG_ERRATA1 0x03
+#define QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL 0x02
+
+/*
+ * Return values from the SCM calls.
+ */
+#define QCOM_SCM_RETVAL_V2_EBUSY -12
+#define QCOM_SCM_RETVAL_ENOMEM -5
+#define QCOM_SCM_RETVAL_EOPNOTSUPP -4
+#define QCOM_SCM_RETVAL_EINVAL_ADDR -3
+#define QCOM_SCM_RETVAL_EINVAL_ARG -2
+#define QCOM_SCM_RETVAL_ERROR -1
+#define QCOM_SCM_RETVAL_INTERRUPTED 1
+
+#endif /* __QCOM_SCM_DEFS_H__ */
Index: sys/arm/qualcomm/qcom_scm_legacy.h
===================================================================
--- sys/arm/qualcomm/qcom_scm_legacy.h
+++ sys/arm/qualcomm/qcom_scm_legacy.h
@@ -27,16 +27,15 @@
* $FreeBSD$
*/
-#ifndef IPQ4018_REG_H
-#define IPQ4018_REG_H
+#ifndef __QCOM_SCM_LEGACY_H__
+#define __QCOM_SCM_LEGACY_H__
-#define IPQ4018_MEM_SMEM_START 0x87e00000
-#define IPQ4018_MEM_SMEM_SIZE 0x00080000
-
-#define IPQ4018_MEM_TZ_START 0x87e80000
-#define IPQ4018_MEM_TZ_SIZE 0x00180000
+/*
+ * These functions are specific to the 32 bit legacy SCM interface
+ * used by the IPQ806x and IPQ401x SoCs.
+ */
-#define IPQ4018_MEM_UART1_START 0x078af000
-#define IPQ4018_MEM_UART1_SIZE 0x00001000
+extern uint32_t qcom_scm_legacy_mp_set_cold_boot_address(
+ vm_offset_t mp_entry_func);
-#endif
+#endif /* __QCOM_SCM_LEGACY_H__ */
Index: sys/arm/qualcomm/qcom_scm_legacy.c
===================================================================
--- sys/arm/qualcomm/qcom_scm_legacy.c
+++ sys/arm/qualcomm/qcom_scm_legacy.c
@@ -35,27 +35,54 @@
#include <sys/bus.h>
#include <sys/reboot.h>
#include <sys/devmap.h>
+#include <sys/smp.h>
#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/cpu.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/machdep.h>
-#include <machine/platformvar.h>
+#include <machine/smp.h>
-#include <dev/fdt/fdt_common.h>
-#include <dev/ofw/openfirm.h>
+#include <arm/qualcomm/qcom_scm_defs.h>
+#include <arm/qualcomm/qcom_scm_legacy_defs.h>
+#include <arm/qualcomm/qcom_scm_legacy.h>
-#include <arm/qualcomm/ipq4018_machdep.h>
+#include <dev/psci/smccc.h>
-#include "platform_if.h"
-
-void
-ipq4018_mp_setmaxid(platform_t plat)
+/*
+ * Set the cold boot address for (later) a mask of CPUs.
+ *
+ * Don't set it for CPU0, that CPU is the boot CPU and is already alive.
+ *
+ * For now it sets it on CPU1..3.
+ *
+ * This works on the IPQ4019 as tested; the retval is 0x0.
+ */
+uint32_t
+qcom_scm_legacy_mp_set_cold_boot_address(vm_offset_t mp_entry_func)
{
-}
+ struct arm_smccc_res res;
+ int ret;
+ int context_id;
-void
-ipq4018_mp_start_ap(platform_t plat)
-{
+ uint32_t scm_arg0 = QCOM_SCM_LEGACY_ATOMIC_ID(QCOM_SCM_SVC_BOOT,
+ QCOM_SCM_BOOT_SET_ADDR, 2);
+
+ uint32_t scm_arg1 = QCOM_SCM_FLAG_COLDBOOT_CPU1
+ | QCOM_SCM_FLAG_COLDBOOT_CPU2
+ | QCOM_SCM_FLAG_COLDBOOT_CPU3;
+ uint32_t scm_arg2 = pmap_kextract((vm_offset_t)mp_entry_func);
+
+ ret = arm_smccc_smc(scm_arg0, (uint32_t) &context_id, scm_arg1,
+ scm_arg2, 0, 0, 0, 0, &res);
+
+ if (ret == 0 && res.a0 == 0)
+ return (0);
+ printf("%s: called; error; ret=0x%08x; retval[0]=0x%08x\n",
+ __func__, ret, res.a0);
+
+ return (0);
}
Index: sys/arm/qualcomm/qcom_scm_legacy_defs.h
===================================================================
--- /dev/null
+++ sys/arm/qualcomm/qcom_scm_legacy_defs.h
@@ -0,0 +1,149 @@
+/*-
+ * 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_SCM_LEGACY_DEFS_H__
+#define __QCOM_SCM_LEGACY_DEFS_H__
+
+/*
+ * These definitions are specific to the 32 bit legacy SCM interface
+ * used by the IPQ806x and IPQ401x SoCs.
+ */
+
+/*
+ * Mapping of the SCM service/command fields into the a0 argument
+ * in an SMC instruction call.
+ *
+ * This is particular to the legacy SCM interface, and is not the
+ * same as the non-legacy 32/64 bit FNID mapping layout.
+ */
+#define QCOM_SCM_LEGACY_SMC_FNID(s, c) (((s) << 10) | ((c) & 0x3ff))
+
+/*
+ * There are two kinds of SCM calls in this legacy path.
+ *
+ * The first kind are the normal ones - up to a defined max of arguments,
+ * a defined max of responses and some identifiers for all of it.
+ * They can be issues in parallel on different cores, can be interrupted,
+ * etc.
+ *
+ * The second kind are what are termed "atomic" SCM calls -
+ * up to 5 argument DWORDs, up to 3 response DWORDs, done atomically,
+ * not interruptable/parallel.
+ *
+ * The former use the structures below to represent the request and response
+ * in memory. The latter use defines and a direct SMC call with the
+ * arguments in registers.
+ */
+
+struct qcom_scm_legacy_smc_args {
+ uint32_t args[8];
+};
+
+/*
+ * Atomic SCM call command/response buffer definitions.
+ */
+#define QCOM_SCM_LEGACY_ATOMIC_MAX_ARGCOUNT 5
+#define QCOM_SCM_LEGACY_CLASS_REGISTER (0x2 << 8)
+#define QCOM_SCM_LEGACY_MASK_IRQS (1U << 5)
+
+/*
+ * Mapping an SCM service/command/argcount into the a0 register
+ * for an SMC instruction call.
+ */
+#define QCOM_SCM_LEGACY_ATOMIC_ID(svc, cmd, n) \
+ ((QCOM_SCM_LEGACY_SMC_FNID((svc), cmd) << 12) | \
+ QCOM_SCM_LEGACY_CLASS_REGISTER | \
+ QCOM_SCM_LEGACY_MASK_IRQS | \
+ ((n) & 0xf))
+
+/*
+ * Legacy command/response buffer definitions.
+ *
+ * The legacy path contains up to the defined maximum arguments
+ * but only a single command/response pair per call.
+ *
+ * A command and response buffer is laid out in memory as such:
+ *
+ * | command header |
+ * | (buffer payload) |
+ * | response header |
+ * | (response payload) |
+ */
+
+/*
+ * The command header.
+ *
+ * len - the length of the total command and response, including
+ * the headers.
+ *
+ * buf_offset - the offset inside the buffer, starting at the
+ * beginning of this command header, where the command buffer
+ * is found. The end is the byte before the response_header_offset.
+ *
+ * response_header_offset - the offset inside the buffer where
+ * the response header is found.
+ *
+ * id - the QCOM_SCM_LEGACY_SMC_FNID() - service/command ids
+ */
+struct qcom_scm_legacy_command_header {
+ uint32_t len;
+ uint32_t buf_offset;
+ uint32_t response_header_offset;
+ uint32_t id;
+};
+
+/*
+ * The response header.
+ *
+ * This is found immediately after the command header and command
+ * buffer payload.
+ *
+ * len - the total amount of memory available for the response.
+ * Linux doesn't set this; it always passes in a response
+ * buffer large enough to store MAX_QCOM_SCM_RETS * DWORD
+ * bytes.
+ *
+ * It's also possible this is set by the firmware.
+ *
+ * buf_offset - start of response buffer, relative to the beginning
+ * of the command header. This also isn't set in Linux before
+ * calling the SMC instruction, but it is checked afterwards
+ * to assemble a pointer to the response data. The firmware
+ * likely sets this.
+ *
+ * is_complete - true if complete. Linux loops over DMA sync to
+ * check if this is complete even after the SMC call returns.
+ */
+struct qcom_scm_legacy_response_header {
+ uint32_t len;
+ uint32_t buf_offset;
+ uint32_t is_complete;
+};
+
+#endif /* __QCOM_SCM_LEGACY_DEFS_H__ */
Index: sys/arm/qualcomm/std.ipq4018
===================================================================
--- sys/arm/qualcomm/std.ipq4018
+++ sys/arm/qualcomm/std.ipq4018
@@ -1,2 +1,9 @@
arm/qualcomm/ipq4018_machdep.c standard
arm/qualcomm/ipq4018_mp.c optional smp
+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
+
Index: sys/dev/qcom_rnd/qcom_rnd.c
===================================================================
--- /dev/null
+++ sys/dev/qcom_rnd/qcom_rnd.c
@@ -0,0 +1,257 @@
+/*-
+ * 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 MSM entropy 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 <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/random/randomdev.h>
+#include <dev/random/random_harvestq.h>
+
+#include <dev/qcom_rnd/qcom_rnd_reg.h>
+
+struct qcom_rnd_softc {
+ device_t dev;
+ int reg_rid;
+ struct resource *reg;
+};
+
+static int qcom_rnd_modevent(module_t, int, void *);
+
+static int qcom_rnd_probe(device_t);
+static int qcom_rnd_attach(device_t);
+static int qcom_rnd_detach(device_t);
+
+static int qcom_rnd_harvest(struct qcom_rnd_softc *, void *, size_t *);
+static unsigned qcom_rnd_read(void *, unsigned);
+
+static struct random_source random_qcom_rnd = {
+ .rs_ident = "Qualcomm Entropy Adapter",
+ .rs_source = RANDOM_PURE_QUALCOMM,
+ .rs_read = qcom_rnd_read,
+};
+
+/* Kludge for API limitations of random(4). */
+static _Atomic(struct qcom_rnd_softc *) g_qcom_rnd_softc;
+
+static int
+qcom_rnd_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_rnd_probe(device_t dev)
+{
+ if (! ofw_bus_status_okay(dev)) {
+ return (ENXIO);
+ }
+
+ if (ofw_bus_is_compatible(dev, "qcom,prng") == 0) {
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+qcom_rnd_attach(device_t dev)
+{
+ struct qcom_rnd_softc *sc, *exp;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+
+
+ /* Found a compatible device! */
+
+ sc->dev = dev;
+
+ exp = NULL;
+ if (!atomic_compare_exchange_strong_explicit(&g_qcom_rnd_softc, &exp,
+ sc, memory_order_release, memory_order_acquire)) {
+ return (ENXIO);
+ }
+
+ sc->reg_rid = 0;
+ sc->reg = bus_alloc_resource_anywhere(dev, SYS_RES_MEMORY,
+ &sc->reg_rid, 0x140, RF_ACTIVE);
+ if (sc->reg == NULL) {
+ device_printf(dev, "Couldn't allocate memory resource!\n");
+ return (ENXIO);
+ }
+
+ device_set_desc(dev, "Qualcomm PRNG");
+
+ /*
+ * Check to see whether the PRNG has already been setup or not.
+ */
+ bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_READ);
+ reg = bus_read_4(sc->reg, QCOM_RND_PRNG_CONFIG);
+ if (reg & QCOM_RND_PRNG_CONFIG_HW_ENABLE) {
+ device_printf(dev, "PRNG HW already enabled\n");
+ } else {
+ /*
+ * Do PRNG setup and then enable it.
+ */
+ reg = bus_read_4(sc->reg, QCOM_RND_PRNG_LFSR_CFG);
+ reg &= QCOM_RND_PRNG_LFSR_CFG_MASK;
+ reg |= QCOM_RND_PRNG_LFSR_CFG_CLOCKS;
+ bus_write_4(sc->reg, QCOM_RND_PRNG_LFSR_CFG, reg);
+ bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_WRITE);
+
+ reg = bus_read_4(sc->reg, QCOM_RND_PRNG_CONFIG);
+ reg |= QCOM_RND_PRNG_CONFIG_HW_ENABLE;
+ bus_write_4(sc->reg, QCOM_RND_PRNG_CONFIG, reg);
+ bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_WRITE);
+ }
+
+ random_source_register(&random_qcom_rnd);
+ return (0);
+
+}
+
+static int
+qcom_rnd_detach(device_t dev)
+{
+ struct qcom_rnd_softc *sc;
+
+ sc = device_get_softc(dev);
+ KASSERT(
+ atomic_load_explicit(&g_qcom_rnd_softc, memory_order_acquire) == sc,
+ ("only one global instance at a time"));
+
+ random_source_deregister(&random_qcom_rnd);
+ if (sc->reg != NULL) {
+ bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->reg_rid, sc->reg);
+ }
+ atomic_store_explicit(&g_qcom_rnd_softc, NULL, memory_order_release);
+ return (0);
+}
+
+static int
+qcom_rnd_harvest(struct qcom_rnd_softc *sc, void *buf, size_t *sz)
+{
+ /*
+ * Add data to buf until we either run out of entropy or we
+ * fill the buffer.
+ *
+ * Note - be mindful of the provided buffer size; we're reading
+ * 4 bytes at a time but we only want to supply up to the max
+ * buffer size, so don't write past it!
+ */
+ size_t rz = 0;
+ uint32_t reg;
+
+ while (rz < *sz) {
+ bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_READ);
+ reg = bus_read_4(sc->reg, QCOM_RND_PRNG_STATUS);
+ if ((reg & QCOM_RND_PRNG_STATUS_DATA_AVAIL) == 0)
+ break;
+ reg = bus_read_4(sc->reg, QCOM_RND_PRNG_DATA_OUT);
+ memcpy(((char *) buf) + rz, ®, sizeof(uint32_t));
+ rz += sizeof(uint32_t);
+ }
+
+ if (rz == 0)
+ return (EAGAIN);
+ *sz = rz;
+ return (0);
+}
+
+static unsigned
+qcom_rnd_read(void *buf, unsigned usz)
+{
+ struct qcom_rnd_softc *sc;
+ size_t sz;
+ int error;
+
+ sc = g_qcom_rnd_softc;
+ if (sc == NULL)
+ return (0);
+
+ sz = usz;
+ error = qcom_rnd_harvest(sc, buf, &sz);
+ if (error != 0)
+ return (0);
+
+ return (sz);
+}
+
+static device_method_t qcom_rnd_methods[] = {
+ /* Device methods. */
+ DEVMETHOD(device_probe, qcom_rnd_probe),
+ DEVMETHOD(device_attach, qcom_rnd_attach),
+ DEVMETHOD(device_detach, qcom_rnd_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t qcom_rnd_driver = {
+ "qcom_rnd",
+ qcom_rnd_methods,
+ sizeof(struct qcom_rnd_softc)
+};
+static devclass_t qcom_rnd_devclass;
+
+DRIVER_MODULE(qcom_rnd_random, simplebus, qcom_rnd_driver, qcom_rnd_devclass,
+ qcom_rnd_modevent, 0);
+DRIVER_MODULE(qcom_rnd_random, ofwbus, qcom_rnd_driver, qcom_rnd_devclass,
+ qcom_rnd_modevent, 0);
+MODULE_DEPEND(qcom_rnd_random, random_device, 1, 1, 1);
+MODULE_VERSION(qcom_rnd_random, 1);
Index: sys/dev/qcom_rnd/qcom_rnd_reg.h
===================================================================
--- /dev/null
+++ sys/dev/qcom_rnd/qcom_rnd_reg.h
@@ -0,0 +1,43 @@
+/*-
+ * 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.
+ */
+
+#ifndef __QCOM_RND_REG_H__
+#define __QCOM_RND_REG_H__
+
+#define QCOM_RND_PRNG_DATA_OUT 0x0000
+
+#define QCOM_RND_PRNG_STATUS 0x0004
+#define QCOM_RND_PRNG_STATUS_DATA_AVAIL (1 << 0)
+
+#define QCOM_RND_PRNG_LFSR_CFG 0x0100
+#define QCOM_RND_PRNG_LFSR_CFG_MASK 0x0000ffff
+#define QCOM_RND_PRNG_LFSR_CFG_CLOCKS 0x0000dddd
+
+#define QCOM_RND_PRNG_CONFIG 0x0104
+#define QCOM_RND_PRNG_CONFIG_HW_ENABLE (1 << 1)
+
+#endif /* __QCOM_RND_REG_H__ */
Index: sys/sys/random.h
===================================================================
--- sys/sys/random.h
+++ sys/sys/random.h
@@ -102,6 +102,7 @@
RANDOM_PURE_DARN,
RANDOM_PURE_TPM,
RANDOM_PURE_VMGENID,
+ RANDOM_PURE_QUALCOMM,
ENTROPYSOURCE
};
_Static_assert(ENTROPYSOURCE <= 32,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Feb 9, 2:26 AM (8 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28526232
Default Alt Text
D32723.id97903.diff (47 KB)
Attached To
Mode
D32723: ipq4018: add SoC reset, SMP, reset syscon and qcom_rnd driver
Attached
Detach File
Event Timeline
Log In to Comment