Home | History | Annotate | Line # | Download | only in fdt
      1 /* $NetBSD: cpu_fdt.c,v 1.4 2024/01/01 13:51:56 skrll Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2017 Jared McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include "opt_multiprocessor.h"
     30 
     31 #include <sys/cdefs.h>
     32 __KERNEL_RCSID(0, "$NetBSD: cpu_fdt.c,v 1.4 2024/01/01 13:51:56 skrll Exp $");
     33 
     34 #include <sys/param.h>
     35 #include <sys/cpu.h>
     36 
     37 #include <dev/fdt/fdtvar.h>
     38 
     39 #include <riscv/cpufunc.h>
     40 #include <riscv/cpuvar.h>
     41 #include <riscv/machdep.h>
     42 #include <riscv/sbi.h>
     43 
     44 #include <riscv/fdt/riscv_fdtvar.h>
     45 
     46 bool
     47 riscv_fdt_cpu_okay(const int node)
     48 {
     49 	const char *s;
     50 
     51 	s = fdtbus_get_string(node, "device_type");
     52 	if (!s || strcmp(s, "cpu") != 0)
     53 		return false;
     54 
     55 	s = fdtbus_get_string(node, "status");
     56 	if (s) {
     57 		if (strcmp(s, "okay") == 0)
     58 			return true;
     59 		if (strcmp(s, "disabled") == 0)
     60 			return false;
     61 		return false;
     62 	} else {
     63 		return true;
     64 	}
     65 }
     66 
     67 void
     68 riscv_fdt_cpu_bootstrap(void)
     69 {
     70 	const int cpus = OF_finddevice("/cpus");
     71 	if (cpus == -1) {
     72 		aprint_error("%s: no /cpus node found\n", __func__);
     73 		return;
     74 	}
     75 
     76 	/* Count harts and add hart index numbers to the cpu_hartindex array */
     77 	u_int cpuindex = 1;
     78 	for (int child = OF_child(cpus); child; child = OF_peer(child)) {
     79 		if (!riscv_fdt_cpu_okay(child))
     80 			continue;
     81 
     82 		uint64_t reg;
     83 		if (fdtbus_get_reg64(child, 0, &reg, NULL) != 0)
     84 			continue;
     85 
     86 		const cpuid_t hartid = reg;
     87 		if (hartid > MAXCPUS) {
     88 			aprint_error("hart id too big %lu (%u)", hartid,
     89 			    MAXCPUS);
     90 			continue;
     91 		}
     92 
     93 		struct sbiret sbiret = sbi_hart_get_status(hartid);
     94 		switch (sbiret.error) {
     95 		case SBI_ERR_INVALID_PARAM:
     96 			aprint_error("Unknown hart id %lx", hartid);
     97 			continue;
     98 		case SBI_SUCCESS:
     99 			break;
    100 		default:
    101 			aprint_error("Unexpected error (%ld) from get_status",
    102 			    sbiret.error);
    103 		}
    104 
    105 		/* Assume the BP is the only one started. */
    106 		if (sbiret.value == SBI_HART_STARTED) {
    107 			if (cpu_bphartid != ~0UL) {
    108 				panic("more than 1 hart started");
    109 			}
    110 			cpu_bphartid = hartid;
    111 			cpu_hartindex[hartid] = 0;
    112 			continue;
    113 		}
    114 
    115 		KASSERT(cpuindex < MAXCPUS);
    116 		cpu_hartindex[hartid] = cpuindex++;
    117 	}
    118 }
    119 
    120 int
    121 riscv_fdt_cpu_mpstart(void)
    122 {
    123 	int ret = 0;
    124 #ifdef MULTIPROCESSOR
    125 	const int cpus = OF_finddevice("/cpus");
    126 	if (cpus == -1) {
    127 		aprint_error("%s: no /cpus node found\n", __func__);
    128 		return 0;
    129 	}
    130 
    131 	/* BootAPs */
    132 	u_int cpuindex = 1;
    133 	for (int child = OF_child(cpus); child; child = OF_peer(child)) {
    134 		if (!riscv_fdt_cpu_okay(child))
    135 			continue;
    136 
    137 		uint64_t reg;
    138 		if (fdtbus_get_reg64(child, 0, &reg, NULL) != 0)
    139 			continue;
    140 
    141 		const cpuid_t hartid = reg;
    142 		if (hartid == cpu_bphartid)
    143 			continue;		/* BP already started */
    144 
    145 		const paddr_t entry = KERN_VTOPHYS(cpu_mpstart);
    146 		struct sbiret sbiret = sbi_hart_start(hartid, entry, cpuindex);
    147 		switch (sbiret.error) {
    148 		case SBI_SUCCESS:
    149 			break;
    150 		case SBI_ERR_INVALID_ADDRESS:
    151 			break;
    152 		case SBI_ERR_INVALID_PARAM:
    153 			break;
    154 		case SBI_ERR_ALREADY_AVAILABLE:
    155 			break;
    156 		case SBI_ERR_FAILED:
    157 			break;
    158 		default:
    159 			aprint_error("%s: failed to enable CPU %#lx\n",
    160 			    __func__, hartid);
    161 		}
    162 
    163 		size_t i;
    164 		/* Wait for AP to start */
    165 		for (i = 0x10000000; i > 0; i--) {
    166 			if (cpu_hatched_p(cpuindex))
    167 				break;
    168 		}
    169 
    170 		if (i == 0) {
    171 			ret++;
    172 			aprint_error("hart%ld: WARNING: AP %u failed to start\n",
    173 			    hartid, cpuindex);
    174 		}
    175 
    176 		cpuindex++;
    177 	}
    178 #else
    179 	aprint_normal("%s: kernel compiled without MULTIPROCESSOR\n", __func__);
    180 #endif /* MULTIPROCESSOR */
    181 	return ret;
    182 }
    183 
    184 static int
    185 cpu_fdt_match(device_t parent, cfdata_t cf, void *aux)
    186 {
    187 	struct fdt_attach_args * const faa = aux;
    188 	const int phandle = faa->faa_phandle;
    189 	const char *device_type;
    190 
    191 	device_type = fdtbus_get_string(phandle, "device_type");
    192 	return device_type != NULL && strcmp(device_type, "cpu") == 0;
    193 }
    194 
    195 static void
    196 cpu_fdt_attach(device_t parent, device_t self, void *aux)
    197 {
    198 	struct fdt_attach_args * const faa = aux;
    199 	const int phandle = faa->faa_phandle;
    200 	bus_addr_t hartid;
    201 
    202 
    203 	if (fdtbus_get_reg(phandle, 0, &hartid, NULL) != 0)
    204 		hartid = 0;
    205 
    206 	/* Attach the CPU */
    207 	cpu_attach(self, hartid);
    208 
    209 	fdt_add_bus(self, phandle, faa);
    210 }
    211 
    212 CFATTACH_DECL_NEW(cpu_fdt, 0, cpu_fdt_match, cpu_fdt_attach, NULL, NULL);
    213