NetBSD/sys/arch/arm/fdt/cpu_fdt.c
skrll 69120ac14e Provide and use a sev() macro for the sev instruction.
While here use the correct barrier to ensure completion of memory accesses
before a couple of the sev() calls.
2020-12-03 07:45:51 +00:00

378 lines
8.8 KiB
C

/* $NetBSD: cpu_fdt.c,v 1.38 2020/12/03 07:45:52 skrll Exp $ */
/*-
* Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
* All rights reserved.
*
* 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 ``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.
*/
#include "opt_multiprocessor.h"
#include "psci_fdt.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cpu_fdt.c,v 1.38 2020/12/03 07:45:52 skrll Exp $");
#include <sys/param.h>
#include <sys/atomic.h>
#include <sys/bus.h>
#include <sys/device.h>
#include <sys/lwp.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <dev/fdt/fdtvar.h>
#include <arm/armreg.h>
#include <arm/cpu.h>
#include <arm/cpufunc.h>
#include <arm/cpuvar.h>
#include <arm/locore.h>
#include <arm/arm/psci.h>
#include <arm/fdt/arm_fdtvar.h>
#include <arm/fdt/psci_fdtvar.h>
#include <uvm/uvm_extern.h>
static int cpu_fdt_match(device_t, cfdata_t, void *);
static void cpu_fdt_attach(device_t, device_t, void *);
struct cpu_fdt_softc {
device_t sc_dev;
int sc_phandle;
};
CFATTACH_DECL_NEW(cpu_fdt, sizeof(struct cpu_fdt_softc),
cpu_fdt_match, cpu_fdt_attach, NULL, NULL);
static int
cpu_fdt_match(device_t parent, cfdata_t cf, void *aux)
{
struct fdt_attach_args * const faa = aux;
const int phandle = faa->faa_phandle;
const char *device_type;
device_type = fdtbus_get_string(phandle, "device_type");
return device_type != NULL && strcmp(device_type, "cpu") == 0;
}
static void
cpu_fdt_attach(device_t parent, device_t self, void *aux)
{
struct cpu_fdt_softc * const sc = device_private(self);
struct fdt_attach_args * const faa = aux;
const int phandle = faa->faa_phandle;
bus_addr_t cpuid;
const uint32_t *cap_ptr;
int len;
sc->sc_dev = self;
sc->sc_phandle = phandle;
cap_ptr = fdtbus_get_prop(phandle, "capacity-dmips-mhz", &len);
if (cap_ptr && len == 4) {
prop_dictionary_t dict = device_properties(self);
uint32_t capacity_dmips_mhz = be32toh(*cap_ptr);
prop_dictionary_set_uint32(dict, "capacity_dmips_mhz",
capacity_dmips_mhz);
}
if (fdtbus_get_reg(phandle, 0, &cpuid, NULL) != 0)
cpuid = 0;
/* Attach the CPU */
cpu_attach(self, cpuid);
/* Attach CPU frequency scaling provider */
config_found(self, faa, NULL);
}
#if defined(MULTIPROCESSOR) && (NPSCI_FDT > 0 || defined(__aarch64__))
static register_t
cpu_fdt_mpstart_pa(void)
{
bool ok __diagused;
paddr_t pa;
ok = pmap_extract(pmap_kernel(), (vaddr_t)cpu_mpstart, &pa);
KASSERT(ok);
return pa;
}
#endif
#ifdef MULTIPROCESSOR
static bool
arm_fdt_cpu_okay(const int child)
{
const char *s;
s = fdtbus_get_string(child, "device_type");
if (!s || strcmp(s, "cpu") != 0)
return false;
s = fdtbus_get_string(child, "status");
if (s) {
if (strcmp(s, "okay") == 0)
return false;
if (strcmp(s, "disabled") == 0)
return of_hasprop(child, "enable-method");
return false;
} else {
return true;
}
}
#endif /* MULTIPROCESSOR */
void
arm_fdt_cpu_bootstrap(void)
{
#ifdef MULTIPROCESSOR
uint64_t mpidr, bp_mpidr;
u_int cpuindex;
int child;
const int cpus = OF_finddevice("/cpus");
if (cpus == -1) {
aprint_error("%s: no /cpus node found\n", __func__);
arm_cpu_max = 1;
return;
}
/* Count CPUs */
arm_cpu_max = 0;
/* MPIDR affinity levels of boot processor. */
bp_mpidr = cpu_mpidr_aff_read();
/* Add APs to cpu_mpidr array */
cpuindex = 1;
for (child = OF_child(cpus); child; child = OF_peer(child)) {
if (!arm_fdt_cpu_okay(child))
continue;
arm_cpu_max++;
if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0)
continue;
if (mpidr == bp_mpidr)
continue; /* BP already started */
KASSERT(cpuindex < MAXCPUS);
cpu_mpidr[cpuindex] = mpidr;
cpu_dcache_wb_range((vaddr_t)&cpu_mpidr[cpuindex],
sizeof(cpu_mpidr[cpuindex]));
cpuindex++;
}
#endif
}
#ifdef MULTIPROCESSOR
static struct arm_cpu_method *
arm_fdt_cpu_enable_method_byname(const char *method)
{
__link_set_decl(arm_cpu_methods, struct arm_cpu_method);
struct arm_cpu_method * const *acmp;
__link_set_foreach(acmp, arm_cpu_methods) {
if (strcmp(method, (*acmp)->acm_compat) == 0)
return *acmp;
}
return NULL;
}
static struct arm_cpu_method *
arm_fdt_cpu_enable_method(int phandle)
{
const char *method;
method = fdtbus_get_string(phandle, "enable-method");
if (method == NULL)
return NULL;
return arm_fdt_cpu_enable_method_byname(method);
}
static int
arm_fdt_cpu_enable(int phandle, struct arm_cpu_method *acm)
{
return acm->acm_enable(phandle);
}
#endif
int
arm_fdt_cpu_mpstart(void)
{
int ret = 0;
#ifdef MULTIPROCESSOR
uint64_t mpidr, bp_mpidr;
u_int cpuindex, i;
int child, error;
struct arm_cpu_method *acm;
const int cpus = OF_finddevice("/cpus");
if (cpus == -1) {
aprint_error("%s: no /cpus node found\n", __func__);
return 0;
}
/* MPIDR affinity levels of boot processor. */
bp_mpidr = cpu_mpidr_aff_read();
/* Boot APs */
cpuindex = 1;
for (child = OF_child(cpus); child; child = OF_peer(child)) {
if (!arm_fdt_cpu_okay(child))
continue;
if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0)
continue;
if (mpidr == bp_mpidr)
continue; /* BP already started */
acm = arm_fdt_cpu_enable_method(child);
if (acm == NULL)
acm = arm_fdt_cpu_enable_method(cpus);
if (acm == NULL)
acm = arm_fdt_cpu_enable_method_byname("psci");
if (acm == NULL)
continue;
error = arm_fdt_cpu_enable(child, acm);
if (error != 0) {
aprint_error("%s: failed to enable CPU %#" PRIx64 "\n",
__func__, mpidr);
continue;
}
/* Wake up AP in case firmware has placed it in WFE state */
sev();
/* Wait for AP to start */
for (i = 0x10000000; i > 0; i--) {
if (cpu_hatched_p(cpuindex))
break;
}
if (i == 0) {
ret++;
aprint_error("cpu%d: WARNING: AP failed to start\n", cpuindex);
}
cpuindex++;
}
#endif /* MULTIPROCESSOR */
return ret;
}
static int
cpu_enable_nullop(int phandle)
{
return ENXIO;
}
ARM_CPU_METHOD(default, "", cpu_enable_nullop);
#if defined(MULTIPROCESSOR) && NPSCI_FDT > 0
static int
cpu_enable_psci(int phandle)
{
static bool psci_probed, psci_p;
uint64_t mpidr;
int ret;
if (!psci_probed) {
psci_probed = true;
psci_p = psci_fdt_preinit() == 0;
}
if (!psci_p)
return ENXIO;
fdtbus_get_reg64(phandle, 0, &mpidr, NULL);
#if !defined(AARCH64)
/*
* not necessary on AARCH64. beside there it hangs the system
* because cache ops are only functional after cpu_attach()
* was called.
*/
cpu_dcache_wbinv_all();
#endif
ret = psci_cpu_on(mpidr, cpu_fdt_mpstart_pa(), 0);
if (ret != PSCI_SUCCESS)
return EIO;
return 0;
}
ARM_CPU_METHOD(psci, "psci", cpu_enable_psci);
#endif
#if defined(MULTIPROCESSOR) && defined(__aarch64__)
static int
spintable_cpu_on(u_int cpuindex, paddr_t entry_point_address, paddr_t cpu_release_addr)
{
/*
* we need devmap for cpu-release-addr in advance.
* __HAVE_MM_MD_DIRECT_MAPPED_PHYS nor pmap work at this point.
*/
if (pmap_devmap_find_pa(cpu_release_addr, sizeof(paddr_t)) == NULL) {
aprint_error("%s: devmap for cpu-release-addr"
" 0x%08"PRIxPADDR" required\n", __func__, cpu_release_addr);
return -1;
} else {
extern struct bus_space arm_generic_bs_tag;
bus_space_handle_t ioh;
bus_space_map(&arm_generic_bs_tag, cpu_release_addr,
sizeof(paddr_t), 0, &ioh);
bus_space_write_4(&arm_generic_bs_tag, ioh, 0,
entry_point_address);
bus_space_unmap(&arm_generic_bs_tag, ioh, sizeof(paddr_t));
}
return 0;
}
static int
cpu_enable_spin_table(int phandle)
{
uint64_t mpidr, addr;
int ret;
fdtbus_get_reg64(phandle, 0, &mpidr, NULL);
if (of_getprop_uint64(phandle, "cpu-release-addr", &addr) != 0)
return ENXIO;
ret = spintable_cpu_on(mpidr, cpu_fdt_mpstart_pa(), (paddr_t)addr);
if (ret != 0)
return EIO;
return 0;
}
ARM_CPU_METHOD(spin_table, "spin-table", cpu_enable_spin_table);
#endif