1*7b4ce397Sriastradh /* $NetBSD: cpu_fdt.c,v 1.44 2024/05/10 14:42:21 riastradh Exp $ */
25ec28b37Sjmcneill
35ec28b37Sjmcneill /*-
45ec28b37Sjmcneill * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
55ec28b37Sjmcneill * All rights reserved.
65ec28b37Sjmcneill *
75ec28b37Sjmcneill * Redistribution and use in source and binary forms, with or without
85ec28b37Sjmcneill * modification, are permitted provided that the following conditions
95ec28b37Sjmcneill * are met:
105ec28b37Sjmcneill * 1. Redistributions of source code must retain the above copyright
115ec28b37Sjmcneill * notice, this list of conditions and the following disclaimer.
125ec28b37Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
135ec28b37Sjmcneill * notice, this list of conditions and the following disclaimer in the
145ec28b37Sjmcneill * documentation and/or other materials provided with the distribution.
155ec28b37Sjmcneill *
165ec28b37Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
175ec28b37Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
185ec28b37Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
195ec28b37Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
205ec28b37Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
215ec28b37Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
225ec28b37Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
235ec28b37Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
245ec28b37Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
255ec28b37Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
265ec28b37Sjmcneill * SUCH DAMAGE.
275ec28b37Sjmcneill */
285ec28b37Sjmcneill
2966d31a2dSryo #include "opt_multiprocessor.h"
3066d31a2dSryo #include "psci_fdt.h"
3166d31a2dSryo
325ec28b37Sjmcneill #include <sys/cdefs.h>
33*7b4ce397Sriastradh __KERNEL_RCSID(0, "$NetBSD: cpu_fdt.c,v 1.44 2024/05/10 14:42:21 riastradh Exp $");
345ec28b37Sjmcneill
355ec28b37Sjmcneill #include <sys/param.h>
3666d31a2dSryo #include <sys/atomic.h>
375ec28b37Sjmcneill #include <sys/bus.h>
385ec28b37Sjmcneill #include <sys/device.h>
39ee91b1e5Sskrll #include <sys/lwp.h>
405ec28b37Sjmcneill #include <sys/systm.h>
415ec28b37Sjmcneill #include <sys/kernel.h>
425ec28b37Sjmcneill
435ec28b37Sjmcneill #include <dev/fdt/fdtvar.h>
445ec28b37Sjmcneill
45fe33aa27Sryo #include <arm/armreg.h>
465ec28b37Sjmcneill #include <arm/cpu.h>
47fe33aa27Sryo #include <arm/cpufunc.h>
48e1281176Sskrll #include <arm/cpuvar.h>
4966d31a2dSryo #include <arm/locore.h>
5066d31a2dSryo
5166d31a2dSryo #include <arm/arm/psci.h>
5266d31a2dSryo #include <arm/fdt/arm_fdtvar.h>
5366d31a2dSryo #include <arm/fdt/psci_fdtvar.h>
5466d31a2dSryo
5566d31a2dSryo #include <uvm/uvm_extern.h>
565ec28b37Sjmcneill
575ec28b37Sjmcneill static int cpu_fdt_match(device_t, cfdata_t, void *);
585ec28b37Sjmcneill static void cpu_fdt_attach(device_t, device_t, void *);
595ec28b37Sjmcneill
600a668ec7Spho CFATTACH_DECL2_NEW(cpu_fdt, 0,
610a668ec7Spho cpu_fdt_match, cpu_fdt_attach, NULL, NULL,
620a668ec7Spho cpu_rescan, cpu_childdetached);
635ec28b37Sjmcneill
645ec28b37Sjmcneill static int
cpu_fdt_match(device_t parent,cfdata_t cf,void * aux)655ec28b37Sjmcneill cpu_fdt_match(device_t parent, cfdata_t cf, void *aux)
665ec28b37Sjmcneill {
675ec28b37Sjmcneill struct fdt_attach_args * const faa = aux;
68cb297a9cSjmcneill const int phandle = faa->faa_phandle;
6974b8f332Sjmcneill const char *device_type;
705ec28b37Sjmcneill
7174b8f332Sjmcneill device_type = fdtbus_get_string(phandle, "device_type");
7253334e2eSjmcneill
7374b8f332Sjmcneill return device_type != NULL && strcmp(device_type, "cpu") == 0;
745ec28b37Sjmcneill }
755ec28b37Sjmcneill
765ec28b37Sjmcneill static void
cpu_fdt_attach(device_t parent,device_t self,void * aux)775ec28b37Sjmcneill cpu_fdt_attach(device_t parent, device_t self, void *aux)
785ec28b37Sjmcneill {
795ec28b37Sjmcneill struct fdt_attach_args * const faa = aux;
80cb297a9cSjmcneill const int phandle = faa->faa_phandle;
8174b8f332Sjmcneill bus_addr_t cpuid;
82b0829efdSmrg const uint32_t *cap_ptr;
83b0829efdSmrg int len;
845ec28b37Sjmcneill
85b0829efdSmrg cap_ptr = fdtbus_get_prop(phandle, "capacity-dmips-mhz", &len);
86b0829efdSmrg if (cap_ptr && len == 4) {
87b0829efdSmrg prop_dictionary_t dict = device_properties(self);
88b0829efdSmrg uint32_t capacity_dmips_mhz = be32toh(*cap_ptr);
89b0829efdSmrg
90b0829efdSmrg prop_dictionary_set_uint32(dict, "capacity_dmips_mhz",
91b0829efdSmrg capacity_dmips_mhz);
92b0829efdSmrg }
93b0829efdSmrg
9474b8f332Sjmcneill if (fdtbus_get_reg(phandle, 0, &cpuid, NULL) != 0)
95cb297a9cSjmcneill cpuid = 0;
965ec28b37Sjmcneill
9753334e2eSjmcneill /* Attach the CPU */
98cb297a9cSjmcneill cpu_attach(self, cpuid);
9991a13c78Sjmcneill
10091a13c78Sjmcneill /* Attach CPU frequency scaling provider */
101*7b4ce397Sriastradh config_found(self, faa, NULL, CFARGS(.iattr = "cpu"));
1025ec28b37Sjmcneill }
10366d31a2dSryo
104c2ecc879Sjmcneill #if defined(MULTIPROCESSOR) && (NPSCI_FDT > 0 || defined(__aarch64__))
10566d31a2dSryo static register_t
cpu_fdt_mpstart_pa(void)10666d31a2dSryo cpu_fdt_mpstart_pa(void)
10766d31a2dSryo {
108e6c2e807Sskrll bool ok __diagused;
109e6c2e807Sskrll paddr_t pa;
110e6c2e807Sskrll
111e6c2e807Sskrll ok = pmap_extract(pmap_kernel(), (vaddr_t)cpu_mpstart, &pa);
112e6c2e807Sskrll KASSERT(ok);
113e6c2e807Sskrll
114e6c2e807Sskrll return pa;
11566d31a2dSryo }
116c2ecc879Sjmcneill #endif
11766d31a2dSryo
118c2ecc879Sjmcneill #ifdef MULTIPROCESSOR
119f220cf33Sjmcneill static bool
arm_fdt_cpu_okay(const int child)120f220cf33Sjmcneill arm_fdt_cpu_okay(const int child)
121f220cf33Sjmcneill {
122f220cf33Sjmcneill const char *s;
123f220cf33Sjmcneill
124f220cf33Sjmcneill s = fdtbus_get_string(child, "device_type");
125f220cf33Sjmcneill if (!s || strcmp(s, "cpu") != 0)
126f220cf33Sjmcneill return false;
127f220cf33Sjmcneill
128f220cf33Sjmcneill s = fdtbus_get_string(child, "status");
129f220cf33Sjmcneill if (s) {
130f220cf33Sjmcneill if (strcmp(s, "okay") == 0)
131f220cf33Sjmcneill return false;
132f220cf33Sjmcneill if (strcmp(s, "disabled") == 0)
133f220cf33Sjmcneill return of_hasprop(child, "enable-method");
134f220cf33Sjmcneill return false;
135f220cf33Sjmcneill } else {
136f220cf33Sjmcneill return true;
137f220cf33Sjmcneill }
138f220cf33Sjmcneill }
1399cdcdf4aSjmcneill #endif /* MULTIPROCESSOR */
14066d31a2dSryo
14166d31a2dSryo void
arm_fdt_cpu_bootstrap(void)14266d31a2dSryo arm_fdt_cpu_bootstrap(void)
14366d31a2dSryo {
14466d31a2dSryo #ifdef MULTIPROCESSOR
14566d31a2dSryo uint64_t mpidr, bp_mpidr;
14666d31a2dSryo u_int cpuindex;
147e6c2e807Sskrll int child;
14866d31a2dSryo
14966d31a2dSryo const int cpus = OF_finddevice("/cpus");
15066d31a2dSryo if (cpus == -1) {
15166d31a2dSryo aprint_error("%s: no /cpus node found\n", __func__);
15266d31a2dSryo arm_cpu_max = 1;
15366d31a2dSryo return;
15466d31a2dSryo }
15566d31a2dSryo
15666d31a2dSryo /* Count CPUs */
15766d31a2dSryo arm_cpu_max = 0;
15866d31a2dSryo
15966d31a2dSryo /* MPIDR affinity levels of boot processor. */
16066d31a2dSryo bp_mpidr = cpu_mpidr_aff_read();
16166d31a2dSryo
16262f6b38bSskrll /* Add APs to cpu_mpidr array */
16366d31a2dSryo cpuindex = 1;
16466d31a2dSryo for (child = OF_child(cpus); child; child = OF_peer(child)) {
165f220cf33Sjmcneill if (!arm_fdt_cpu_okay(child))
16666d31a2dSryo continue;
167e6c2e807Sskrll
168e6c2e807Sskrll arm_cpu_max++;
16966d31a2dSryo if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0)
17066d31a2dSryo continue;
17166d31a2dSryo if (mpidr == bp_mpidr)
17266d31a2dSryo continue; /* BP already started */
17366d31a2dSryo
17466d31a2dSryo KASSERT(cpuindex < MAXCPUS);
17566d31a2dSryo cpu_mpidr[cpuindex] = mpidr;
176e6c2e807Sskrll cpu_dcache_wb_range((vaddr_t)&cpu_mpidr[cpuindex],
177e6c2e807Sskrll sizeof(cpu_mpidr[cpuindex]));
178e6c2e807Sskrll
179e6c2e807Sskrll cpuindex++;
180e6c2e807Sskrll }
181e6c2e807Sskrll #endif
182e6c2e807Sskrll }
183e6c2e807Sskrll
1843bb9dca4Sjmcneill #ifdef MULTIPROCESSOR
18532689f21Sjmcneill static struct arm_cpu_method *
arm_fdt_cpu_enable_method_byname(const char * method)1867af73871Sjmcneill arm_fdt_cpu_enable_method_byname(const char *method)
1877af73871Sjmcneill {
1887af73871Sjmcneill __link_set_decl(arm_cpu_methods, struct arm_cpu_method);
1897af73871Sjmcneill struct arm_cpu_method * const *acmp;
1907af73871Sjmcneill
1917af73871Sjmcneill __link_set_foreach(acmp, arm_cpu_methods) {
1927af73871Sjmcneill if (strcmp(method, (*acmp)->acm_compat) == 0)
1937af73871Sjmcneill return *acmp;
1947af73871Sjmcneill }
1957af73871Sjmcneill
1967af73871Sjmcneill return NULL;
1977af73871Sjmcneill }
1987af73871Sjmcneill
1997af73871Sjmcneill static struct arm_cpu_method *
arm_fdt_cpu_enable_method(int phandle)20032689f21Sjmcneill arm_fdt_cpu_enable_method(int phandle)
2013bb9dca4Sjmcneill {
20232689f21Sjmcneill const char *method;
20332689f21Sjmcneill
20432689f21Sjmcneill method = fdtbus_get_string(phandle, "enable-method");
20532689f21Sjmcneill if (method == NULL)
20632689f21Sjmcneill return NULL;
20732689f21Sjmcneill
2087af73871Sjmcneill return arm_fdt_cpu_enable_method_byname(method);
20932689f21Sjmcneill }
21032689f21Sjmcneill
21132689f21Sjmcneill static int
arm_fdt_cpu_enable(int phandle,struct arm_cpu_method * acm)21232689f21Sjmcneill arm_fdt_cpu_enable(int phandle, struct arm_cpu_method *acm)
21332689f21Sjmcneill {
21432689f21Sjmcneill return acm->acm_enable(phandle);
2153bb9dca4Sjmcneill }
2163bb9dca4Sjmcneill #endif
2173bb9dca4Sjmcneill
218a476a90dSskrll int
arm_fdt_cpu_mpstart(void)219e6c2e807Sskrll arm_fdt_cpu_mpstart(void)
220e6c2e807Sskrll {
221a476a90dSskrll int ret = 0;
222e6c2e807Sskrll #ifdef MULTIPROCESSOR
223e6c2e807Sskrll uint64_t mpidr, bp_mpidr;
2243bb9dca4Sjmcneill u_int cpuindex, i;
2253bb9dca4Sjmcneill int child, error;
22632689f21Sjmcneill struct arm_cpu_method *acm;
227e6c2e807Sskrll
228e6c2e807Sskrll const int cpus = OF_finddevice("/cpus");
229e6c2e807Sskrll if (cpus == -1) {
230e6c2e807Sskrll aprint_error("%s: no /cpus node found\n", __func__);
231a476a90dSskrll return 0;
232e6c2e807Sskrll }
233e6c2e807Sskrll
234e6c2e807Sskrll /* MPIDR affinity levels of boot processor. */
235e6c2e807Sskrll bp_mpidr = cpu_mpidr_aff_read();
236e6c2e807Sskrll
237e6c2e807Sskrll /* Boot APs */
238e6c2e807Sskrll cpuindex = 1;
239e6c2e807Sskrll for (child = OF_child(cpus); child; child = OF_peer(child)) {
240e6c2e807Sskrll if (!arm_fdt_cpu_okay(child))
241e6c2e807Sskrll continue;
242e6c2e807Sskrll
243e6c2e807Sskrll if (fdtbus_get_reg64(child, 0, &mpidr, NULL) != 0)
244e6c2e807Sskrll continue;
245030faca8Sskrll
246e6c2e807Sskrll if (mpidr == bp_mpidr)
247e6c2e807Sskrll continue; /* BP already started */
248e6c2e807Sskrll
24932689f21Sjmcneill acm = arm_fdt_cpu_enable_method(child);
25032689f21Sjmcneill if (acm == NULL)
25132689f21Sjmcneill acm = arm_fdt_cpu_enable_method(cpus);
25232689f21Sjmcneill if (acm == NULL)
2537af73871Sjmcneill acm = arm_fdt_cpu_enable_method_byname("psci");
2547af73871Sjmcneill if (acm == NULL)
25566d31a2dSryo continue;
25666d31a2dSryo
25732689f21Sjmcneill error = arm_fdt_cpu_enable(child, acm);
2583bb9dca4Sjmcneill if (error != 0) {
2595776f3ebSskrll aprint_error("%s: failed to enable CPU %#" PRIx64 "\n",
2605776f3ebSskrll __func__, mpidr);
26166d31a2dSryo continue;
26266d31a2dSryo }
26366d31a2dSryo
26466d31a2dSryo /* Wake up AP in case firmware has placed it in WFE state */
26569120ac1Sskrll sev();
26666d31a2dSryo
2673bb9dca4Sjmcneill /* Wait for AP to start */
268912cfa14Sjmcneill for (i = 0x10000000; i > 0; i--) {
26929224e5eSjmcneill if (cpu_hatched_p(cpuindex))
27066d31a2dSryo break;
27166d31a2dSryo }
272a476a90dSskrll
273a476a90dSskrll if (i == 0) {
274a476a90dSskrll ret++;
2753bb9dca4Sjmcneill aprint_error("cpu%d: WARNING: AP failed to start\n", cpuindex);
276a476a90dSskrll }
2773bb9dca4Sjmcneill
2783bb9dca4Sjmcneill cpuindex++;
2793bb9dca4Sjmcneill }
28066d31a2dSryo #endif /* MULTIPROCESSOR */
281a476a90dSskrll return ret;
28266d31a2dSryo }
2833bb9dca4Sjmcneill
2843bb9dca4Sjmcneill static int
cpu_enable_nullop(int phandle)2853bb9dca4Sjmcneill cpu_enable_nullop(int phandle)
2863bb9dca4Sjmcneill {
2873bb9dca4Sjmcneill return ENXIO;
2883bb9dca4Sjmcneill }
2893bb9dca4Sjmcneill ARM_CPU_METHOD(default, "", cpu_enable_nullop);
2903bb9dca4Sjmcneill
2913bb9dca4Sjmcneill #if defined(MULTIPROCESSOR) && NPSCI_FDT > 0
2923bb9dca4Sjmcneill static int
cpu_enable_psci(int phandle)2933bb9dca4Sjmcneill cpu_enable_psci(int phandle)
2943bb9dca4Sjmcneill {
2953bb9dca4Sjmcneill static bool psci_probed, psci_p;
2963bb9dca4Sjmcneill uint64_t mpidr;
2973bb9dca4Sjmcneill int ret;
2983bb9dca4Sjmcneill
2993bb9dca4Sjmcneill if (!psci_probed) {
3003bb9dca4Sjmcneill psci_probed = true;
3013bb9dca4Sjmcneill psci_p = psci_fdt_preinit() == 0;
3023bb9dca4Sjmcneill }
3033bb9dca4Sjmcneill if (!psci_p)
3043bb9dca4Sjmcneill return ENXIO;
3053bb9dca4Sjmcneill
3063bb9dca4Sjmcneill fdtbus_get_reg64(phandle, 0, &mpidr, NULL);
3073bb9dca4Sjmcneill
308f72c8a3cSbad #if !defined(AARCH64)
309f72c8a3cSbad /*
31011c39ba5Sbad * not necessary on AARCH64. beside there it hangs the system
311f72c8a3cSbad * because cache ops are only functional after cpu_attach()
312f72c8a3cSbad * was called.
313f72c8a3cSbad */
314f72c8a3cSbad cpu_dcache_wbinv_all();
315f72c8a3cSbad #endif
3163bb9dca4Sjmcneill ret = psci_cpu_on(mpidr, cpu_fdt_mpstart_pa(), 0);
3173bb9dca4Sjmcneill if (ret != PSCI_SUCCESS)
3183bb9dca4Sjmcneill return EIO;
3193bb9dca4Sjmcneill
3203bb9dca4Sjmcneill return 0;
3213bb9dca4Sjmcneill }
3223bb9dca4Sjmcneill ARM_CPU_METHOD(psci, "psci", cpu_enable_psci);
3233bb9dca4Sjmcneill #endif
3243bb9dca4Sjmcneill
3258eb52256Sjmcneill #if defined(MULTIPROCESSOR) && defined(__aarch64__)
3268eb52256Sjmcneill static int
spintable_cpu_on(const int phandle,u_int cpuindex,paddr_t entry_point_address,paddr_t cpu_release_addr)32723255edfSjmcneill spintable_cpu_on(const int phandle, u_int cpuindex,
32823255edfSjmcneill paddr_t entry_point_address, paddr_t cpu_release_addr)
3298eb52256Sjmcneill {
3308eb52256Sjmcneill /*
3318eb52256Sjmcneill * we need devmap for cpu-release-addr in advance.
332c02d4e44Sskrll * __HAVE_MM_MD_DIRECT_MAPPED_PHYS nor pmap work at this point.
3338eb52256Sjmcneill */
3348eb52256Sjmcneill if (pmap_devmap_find_pa(cpu_release_addr, sizeof(paddr_t)) == NULL) {
3358eb52256Sjmcneill aprint_error("%s: devmap for cpu-release-addr"
3368eb52256Sjmcneill " 0x%08"PRIxPADDR" required\n", __func__, cpu_release_addr);
3378eb52256Sjmcneill return -1;
3388eb52256Sjmcneill } else {
3398eb52256Sjmcneill extern struct bus_space arm_generic_bs_tag;
3408eb52256Sjmcneill bus_space_handle_t ioh;
3418eb52256Sjmcneill
34223255edfSjmcneill const int parent = OF_parent(phandle);
34323255edfSjmcneill const int addr_cells = fdtbus_get_addr_cells(parent);
34423255edfSjmcneill
3458eb52256Sjmcneill bus_space_map(&arm_generic_bs_tag, cpu_release_addr,
3468eb52256Sjmcneill sizeof(paddr_t), 0, &ioh);
34723255edfSjmcneill if (addr_cells == 1) {
3488eb52256Sjmcneill bus_space_write_4(&arm_generic_bs_tag, ioh, 0,
3498eb52256Sjmcneill entry_point_address);
35023255edfSjmcneill } else {
35123255edfSjmcneill bus_space_write_8(&arm_generic_bs_tag, ioh, 0,
35223255edfSjmcneill entry_point_address);
35323255edfSjmcneill }
3548eb52256Sjmcneill bus_space_unmap(&arm_generic_bs_tag, ioh, sizeof(paddr_t));
3558eb52256Sjmcneill }
3568eb52256Sjmcneill
3578eb52256Sjmcneill return 0;
3588eb52256Sjmcneill }
3598eb52256Sjmcneill
3603bb9dca4Sjmcneill static int
cpu_enable_spin_table(int phandle)3613bb9dca4Sjmcneill cpu_enable_spin_table(int phandle)
3623bb9dca4Sjmcneill {
363cdd1c6dfSjmcneill uint64_t mpidr, addr;
3643bb9dca4Sjmcneill int ret;
3653bb9dca4Sjmcneill
3663bb9dca4Sjmcneill fdtbus_get_reg64(phandle, 0, &mpidr, NULL);
3673bb9dca4Sjmcneill
368cdd1c6dfSjmcneill if (of_getprop_uint64(phandle, "cpu-release-addr", &addr) != 0)
3693bb9dca4Sjmcneill return ENXIO;
3703bb9dca4Sjmcneill
37123255edfSjmcneill ret = spintable_cpu_on(phandle, mpidr, cpu_fdt_mpstart_pa(),
37223255edfSjmcneill (paddr_t)addr);
3733bb9dca4Sjmcneill if (ret != 0)
3743bb9dca4Sjmcneill return EIO;
3753bb9dca4Sjmcneill
3763bb9dca4Sjmcneill return 0;
3773bb9dca4Sjmcneill }
3783bb9dca4Sjmcneill ARM_CPU_METHOD(spin_table, "spin-table", cpu_enable_spin_table);
3793bb9dca4Sjmcneill #endif
380