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,9 @@ device uart device uart_msm # Qualcomm MSM UART driver +# Random +device qcom_rnd + device gic # MMC/SD/SDIO Card slot support @@ -22,6 +25,9 @@ device generic_timer device mpcore_timer +# PSCI - SMC calls, needed for qualcomm SCM +device psci + 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_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,6 @@ 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 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,