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 #include #include +#include #include #include +#include #include #include #include @@ -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 #include #include +#include #include +#include #include #include #include #include +#include #include #include +#include #include +#include +#include #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,224 @@ +/*- + * 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. + */ + +#include "opt_platform.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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; + + /* + * Check to make sure we have the right properties. + */ + if (OF_hasprop(node, "qcom,acc") == 0) + panic("*** Doesn't have qcom,acc!"); + + if (OF_hasprop(node, "qcom,saw") == 0) + panic("*** Doesn't have qcom,saw!"); + + if (OF_hasprop(node, "next-level-cache") == 0) + panic("*** Doesn't have next-level-cache!"); + + + /* + * 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,191 @@ +/*- + * 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 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "hwreset_if.h" + +#include + +#include + + +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; +// uint32_t reg; + + 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); + hwreset_register_ofw_provider(dev); + +#if 0 + /* + * 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); +#endif + 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 + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "hwreset_if.h" + +#include + +#include + + +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_reset_reg.h =================================================================== --- /dev/null +++ sys/arm/qualcomm/qcom_gcc_ipq4018_reset_reg.h @@ -0,0 +1,78 @@ +struct qcom_gcc_ipq4018_reset_entry { + uint32_t reg; + uint32_t bit; +}; + +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}, +}; 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 + * + * 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 #include #include +#include #include +#include +#include #include #include #include -#include +#include -#include -#include +#include +#include +#include -#include +#include -#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,103 @@ +/*- + * 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_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)) + +/* + * Normal command/response buffer definitions.. need to describe + * these MUCH better before landing this diff. + */ + +struct qcom_scm_legacy_command_header { + uint32_t len; + uint32_t buf_offset; + uint32_t response_header_offset; + uint32_t id; +}; + +struct qcom_scm_legacy_response_header { + uint32_t len; + uint32_t buffer_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 + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +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 + * + * 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,