xref: /netbsd-src/sys/arch/arm/acpi/cpu_acpi.c (revision 388bf930e98f3210893b768a943a5e120c619053)
1*388bf930Sjmcneill /* $NetBSD: cpu_acpi.c,v 1.17 2024/12/30 19:17:21 jmcneill Exp $ */
297d57f80Sjmcneill 
397d57f80Sjmcneill /*-
497d57f80Sjmcneill  * Copyright (c) 2018 The NetBSD Foundation, Inc.
597d57f80Sjmcneill  * All rights reserved.
697d57f80Sjmcneill  *
797d57f80Sjmcneill  * This code is derived from software contributed to The NetBSD Foundation
897d57f80Sjmcneill  * by Jared McNeill <jmcneill@invisible.ca>.
997d57f80Sjmcneill  *
1097d57f80Sjmcneill  * Redistribution and use in source and binary forms, with or without
1197d57f80Sjmcneill  * modification, are permitted provided that the following conditions
1297d57f80Sjmcneill  * are met:
1397d57f80Sjmcneill  * 1. Redistributions of source code must retain the above copyright
1497d57f80Sjmcneill  *    notice, this list of conditions and the following disclaimer.
1597d57f80Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
1697d57f80Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
1797d57f80Sjmcneill  *    documentation and/or other materials provided with the distribution.
1897d57f80Sjmcneill  *
1997d57f80Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2097d57f80Sjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2197d57f80Sjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2297d57f80Sjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2397d57f80Sjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2497d57f80Sjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2597d57f80Sjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2697d57f80Sjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2797d57f80Sjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2897d57f80Sjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2997d57f80Sjmcneill  * POSSIBILITY OF SUCH DAMAGE.
3097d57f80Sjmcneill  */
3197d57f80Sjmcneill 
32c3589be4Sjmcneill #include "tprof.h"
336f2828aaSryo #include "opt_multiprocessor.h"
34c3589be4Sjmcneill 
3597d57f80Sjmcneill #include <sys/cdefs.h>
36*388bf930Sjmcneill __KERNEL_RCSID(0, "$NetBSD: cpu_acpi.c,v 1.17 2024/12/30 19:17:21 jmcneill Exp $");
3797d57f80Sjmcneill 
3897d57f80Sjmcneill #include <sys/param.h>
3997d57f80Sjmcneill #include <sys/bus.h>
4097d57f80Sjmcneill #include <sys/cpu.h>
4197d57f80Sjmcneill #include <sys/device.h>
42c3589be4Sjmcneill #include <sys/interrupt.h>
43c3589be4Sjmcneill #include <sys/kcpuset.h>
44*388bf930Sjmcneill #include <sys/kmem.h>
4529224e5eSjmcneill #include <sys/reboot.h>
4697d57f80Sjmcneill 
4797d57f80Sjmcneill #include <dev/acpi/acpireg.h>
4897d57f80Sjmcneill #include <dev/acpi/acpivar.h>
49397fdee4Sjmcneill #include <dev/acpi/acpi_srat.h>
50*388bf930Sjmcneill #include <external/bsd/acpica/dist/include/amlresrc.h>
5197d57f80Sjmcneill 
5297d57f80Sjmcneill #include <arm/armreg.h>
5397d57f80Sjmcneill #include <arm/cpu.h>
5497d57f80Sjmcneill #include <arm/cpufunc.h>
55e1281176Sskrll #include <arm/cpuvar.h>
5697d57f80Sjmcneill #include <arm/locore.h>
5797d57f80Sjmcneill 
5897d57f80Sjmcneill #include <arm/arm/psci.h>
5997d57f80Sjmcneill 
60*388bf930Sjmcneill #define LPI_IDLE_FACTOR		3
61*388bf930Sjmcneill 
62c3589be4Sjmcneill #if NTPROF > 0
63c3589be4Sjmcneill #include <dev/tprof/tprof_armv8.h>
64c3589be4Sjmcneill #endif
65c3589be4Sjmcneill 
6697d57f80Sjmcneill static int	cpu_acpi_match(device_t, cfdata_t, void *);
6797d57f80Sjmcneill static void	cpu_acpi_attach(device_t, device_t, void *);
6897d57f80Sjmcneill 
69*388bf930Sjmcneill static void	cpu_acpi_probe_lpi(device_t, struct cpu_info *ci);
70*388bf930Sjmcneill void		cpu_acpi_lpi_idle(void);
71*388bf930Sjmcneill 
72c3589be4Sjmcneill #if NTPROF > 0
73c3589be4Sjmcneill static void	cpu_acpi_tprof_init(device_t);
74c3589be4Sjmcneill #endif
75c3589be4Sjmcneill 
760a668ec7Spho CFATTACH_DECL2_NEW(cpu_acpi, 0,
770a668ec7Spho     cpu_acpi_match, cpu_acpi_attach, NULL, NULL,
780a668ec7Spho     cpu_rescan, cpu_childdetached);
7997d57f80Sjmcneill 
806f2828aaSryo #ifdef MULTIPROCESSOR
8197d57f80Sjmcneill static register_t
8297d57f80Sjmcneill cpu_acpi_mpstart_pa(void)
8397d57f80Sjmcneill {
84e6c2e807Sskrll 
85e6c2e807Sskrll 	return (register_t)KERN_VTOPHYS((vaddr_t)cpu_mpstart);
8697d57f80Sjmcneill }
876f2828aaSryo #endif /* MULTIPROCESSOR */
8897d57f80Sjmcneill 
8997d57f80Sjmcneill static int
9097d57f80Sjmcneill cpu_acpi_match(device_t parent, cfdata_t cf, void *aux)
9197d57f80Sjmcneill {
9297d57f80Sjmcneill 	ACPI_SUBTABLE_HEADER *hdrp = aux;
9373fbce2bSjmcneill 	ACPI_MADT_GENERIC_INTERRUPT *gicc;
9497d57f80Sjmcneill 
9573fbce2bSjmcneill 	if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_INTERRUPT)
9673fbce2bSjmcneill 		return 0;
9773fbce2bSjmcneill 
9873fbce2bSjmcneill 	gicc = (ACPI_MADT_GENERIC_INTERRUPT *)hdrp;
9973fbce2bSjmcneill 
10073fbce2bSjmcneill 	return (gicc->Flags & ACPI_MADT_ENABLED) != 0;
10197d57f80Sjmcneill }
10297d57f80Sjmcneill 
10397d57f80Sjmcneill static void
10497d57f80Sjmcneill cpu_acpi_attach(device_t parent, device_t self, void *aux)
10597d57f80Sjmcneill {
10647ed2bf4Sjmcneill 	prop_dictionary_t dict = device_properties(self);
10797d57f80Sjmcneill 	ACPI_MADT_GENERIC_INTERRUPT *gicc = aux;
10897d57f80Sjmcneill 	const uint64_t mpidr = gicc->ArmMpidr;
10992a57f01Sjmcneill 	const int unit = device_unit(self);
11092a57f01Sjmcneill 	struct cpu_info *ci = &cpu_info_store[unit];
111397fdee4Sjmcneill 	struct acpisrat_node *node;
11297d57f80Sjmcneill 
1136f2828aaSryo #ifdef MULTIPROCESSOR
11429224e5eSjmcneill 	if (cpu_mpidr_aff_read() != mpidr && (boothowto & RB_MD1) == 0) {
11597d57f80Sjmcneill 		const u_int cpuindex = device_unit(self);
1166f2828aaSryo 		int error;
11797d57f80Sjmcneill 
11897d57f80Sjmcneill 		cpu_mpidr[cpuindex] = mpidr;
119999b9562Sjmcneill 		cpu_dcache_wb_range((vaddr_t)&cpu_mpidr[cpuindex],
120999b9562Sjmcneill 		    sizeof(cpu_mpidr[cpuindex]));
12197d57f80Sjmcneill 
12297d57f80Sjmcneill 		/* XXX support spin table */
12397d57f80Sjmcneill 		error = psci_cpu_on(mpidr, cpu_acpi_mpstart_pa(), 0);
12497d57f80Sjmcneill 		if (error != PSCI_SUCCESS) {
12597d57f80Sjmcneill 			aprint_error_dev(self, "failed to start CPU\n");
12697d57f80Sjmcneill 			return;
12797d57f80Sjmcneill 		}
12897d57f80Sjmcneill 
12969120ac1Sskrll 		sev();
13097d57f80Sjmcneill 
13197d57f80Sjmcneill 		for (u_int i = 0x10000000; i > 0; i--) {
13229224e5eSjmcneill 			if (cpu_hatched_p(cpuindex))
13397d57f80Sjmcneill 				 break;
13497d57f80Sjmcneill 		}
13597d57f80Sjmcneill 	}
1366f2828aaSryo #endif /* MULTIPROCESSOR */
13797d57f80Sjmcneill 
13847ed2bf4Sjmcneill 	/* Assume that less efficient processors are faster. */
13947ed2bf4Sjmcneill 	prop_dictionary_set_uint32(dict, "capacity_dmips_mhz",
14047ed2bf4Sjmcneill 	    gicc->EfficiencyClass);
14147ed2bf4Sjmcneill 
14292a57f01Sjmcneill 	/* Store the ACPI Processor UID in cpu_info */
14392a57f01Sjmcneill 	ci->ci_acpiid = gicc->Uid;
14492a57f01Sjmcneill 
145397fdee4Sjmcneill 	/* Scan SRAT for NUMA info. */
146397fdee4Sjmcneill 	if (cpu_mpidr_aff_read() == mpidr) {
147397fdee4Sjmcneill 		acpisrat_init();
148397fdee4Sjmcneill 	}
149397fdee4Sjmcneill 	node = acpisrat_get_node(gicc->Uid);
150397fdee4Sjmcneill 	if (node != NULL) {
151397fdee4Sjmcneill 		ci->ci_numa_id = node->nodeid;
152397fdee4Sjmcneill 	}
153397fdee4Sjmcneill 
15497d57f80Sjmcneill 	/* Attach the CPU */
15597d57f80Sjmcneill 	cpu_attach(self, mpidr);
156c3589be4Sjmcneill 
157*388bf930Sjmcneill 	/* Probe for low-power idle states. */
158*388bf930Sjmcneill 	cpu_acpi_probe_lpi(self, ci);
159*388bf930Sjmcneill 
160c3589be4Sjmcneill #if NTPROF > 0
1616724c56eSjmcneill 	if (cpu_mpidr_aff_read() == mpidr && armv8_pmu_detect())
162c3589be4Sjmcneill 		config_interrupts(self, cpu_acpi_tprof_init);
163c3589be4Sjmcneill #endif
16497d57f80Sjmcneill }
165c3589be4Sjmcneill 
166*388bf930Sjmcneill static void
167*388bf930Sjmcneill cpu_acpi_probe_lpi(device_t dev, struct cpu_info *ci)
168*388bf930Sjmcneill {
169*388bf930Sjmcneill 	ACPI_HANDLE hdl;
170*388bf930Sjmcneill 	ACPI_BUFFER buf;
171*388bf930Sjmcneill 	ACPI_OBJECT *obj, *lpi;
172*388bf930Sjmcneill 	ACPI_STATUS rv;
173*388bf930Sjmcneill 	uint32_t levelid;
174*388bf930Sjmcneill 	uint32_t numlpi;
175*388bf930Sjmcneill 	uint32_t n;
176*388bf930Sjmcneill 	int enable_lpi;
177*388bf930Sjmcneill 
178*388bf930Sjmcneill 	if (get_bootconf_option(boot_args, "nolpi",
179*388bf930Sjmcneill 				BOOTOPT_TYPE_BOOLEAN, &enable_lpi) &&
180*388bf930Sjmcneill 	    !enable_lpi) {
181*388bf930Sjmcneill 		return;
182*388bf930Sjmcneill 	}
183*388bf930Sjmcneill 
184*388bf930Sjmcneill 	hdl = acpi_match_cpu_info(ci);
185*388bf930Sjmcneill 	if (hdl == NULL) {
186*388bf930Sjmcneill 		return;
187*388bf930Sjmcneill 	}
188*388bf930Sjmcneill 	rv = AcpiGetHandle(hdl, "_LPI", &hdl);
189*388bf930Sjmcneill 	if (ACPI_FAILURE(rv)) {
190*388bf930Sjmcneill 		return;
191*388bf930Sjmcneill 	}
192*388bf930Sjmcneill 	rv = acpi_eval_struct(hdl, NULL, &buf);
193*388bf930Sjmcneill 	if (ACPI_FAILURE(rv)) {
194*388bf930Sjmcneill 		return;
195*388bf930Sjmcneill 	}
196*388bf930Sjmcneill 
197*388bf930Sjmcneill 	obj = buf.Pointer;
198*388bf930Sjmcneill 	if (obj->Type != ACPI_TYPE_PACKAGE ||
199*388bf930Sjmcneill 	    obj->Package.Count < 3 ||
200*388bf930Sjmcneill 	    obj->Package.Elements[1].Type != ACPI_TYPE_INTEGER ||
201*388bf930Sjmcneill 	    obj->Package.Elements[2].Type != ACPI_TYPE_INTEGER) {
202*388bf930Sjmcneill 		goto out;
203*388bf930Sjmcneill 	}
204*388bf930Sjmcneill 	levelid = obj->Package.Elements[1].Integer.Value;
205*388bf930Sjmcneill 	if (levelid != 0) {
206*388bf930Sjmcneill 		/* We depend on platform coordination for now. */
207*388bf930Sjmcneill 		goto out;
208*388bf930Sjmcneill 	}
209*388bf930Sjmcneill 	numlpi = obj->Package.Elements[2].Integer.Value;
210*388bf930Sjmcneill 	if (obj->Package.Count < 3 + numlpi || numlpi == 0) {
211*388bf930Sjmcneill 		goto out;
212*388bf930Sjmcneill 	}
213*388bf930Sjmcneill 	ci->ci_lpi = kmem_zalloc(sizeof(*ci->ci_lpi) * numlpi, KM_SLEEP);
214*388bf930Sjmcneill 	for (n = 0; n < numlpi; n++) {
215*388bf930Sjmcneill 		lpi = &obj->Package.Elements[3 + n];
216*388bf930Sjmcneill 		if (lpi->Type != ACPI_TYPE_PACKAGE ||
217*388bf930Sjmcneill 		    lpi->Package.Count < 10 ||
218*388bf930Sjmcneill 		    lpi->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
219*388bf930Sjmcneill 		    lpi->Package.Elements[1].Type != ACPI_TYPE_INTEGER ||
220*388bf930Sjmcneill 		    lpi->Package.Elements[2].Type != ACPI_TYPE_INTEGER ||
221*388bf930Sjmcneill 		    lpi->Package.Elements[3].Type != ACPI_TYPE_INTEGER ||
222*388bf930Sjmcneill 		    !(lpi->Package.Elements[6].Type == ACPI_TYPE_BUFFER ||
223*388bf930Sjmcneill 		      lpi->Package.Elements[6].Type == ACPI_TYPE_INTEGER)) {
224*388bf930Sjmcneill 			continue;
225*388bf930Sjmcneill 		}
226*388bf930Sjmcneill 
227*388bf930Sjmcneill 		if ((lpi->Package.Elements[2].Integer.Value & 1) == 0) {
228*388bf930Sjmcneill 			/* LPI state is not enabled */
229*388bf930Sjmcneill 			continue;
230*388bf930Sjmcneill 		}
231*388bf930Sjmcneill 
232*388bf930Sjmcneill 		ci->ci_lpi[ci->ci_nlpi].min_res
233*388bf930Sjmcneill 		    = lpi->Package.Elements[0].Integer.Value;
234*388bf930Sjmcneill 		ci->ci_lpi[ci->ci_nlpi].wakeup_latency =
235*388bf930Sjmcneill 		    lpi->Package.Elements[1].Integer.Value;
236*388bf930Sjmcneill 		ci->ci_lpi[ci->ci_nlpi].save_restore_flags =
237*388bf930Sjmcneill 		    lpi->Package.Elements[3].Integer.Value;
238*388bf930Sjmcneill 		if (ci->ci_lpi[ci->ci_nlpi].save_restore_flags != 0) {
239*388bf930Sjmcneill 			/* Not implemented yet */
240*388bf930Sjmcneill 			continue;
241*388bf930Sjmcneill 		}
242*388bf930Sjmcneill 		if (lpi->Package.Elements[6].Type == ACPI_TYPE_INTEGER) {
243*388bf930Sjmcneill 			ci->ci_lpi[ci->ci_nlpi].reg_addr =
244*388bf930Sjmcneill 			    lpi->Package.Elements[6].Integer.Value;
245*388bf930Sjmcneill 		} else {
246*388bf930Sjmcneill 			ACPI_GENERIC_ADDRESS addr;
247*388bf930Sjmcneill 
248*388bf930Sjmcneill 			KASSERT(lpi->Package.Elements[6].Type ==
249*388bf930Sjmcneill 				ACPI_TYPE_BUFFER);
250*388bf930Sjmcneill 
251*388bf930Sjmcneill 			if (lpi->Package.Elements[6].Buffer.Length <
252*388bf930Sjmcneill 			    sizeof(AML_RESOURCE_GENERIC_REGISTER)) {
253*388bf930Sjmcneill 				continue;
254*388bf930Sjmcneill 			}
255*388bf930Sjmcneill 			memcpy(&addr, lpi->Package.Elements[6].Buffer.Pointer +
256*388bf930Sjmcneill 			    sizeof(AML_RESOURCE_LARGE_HEADER), sizeof(addr));
257*388bf930Sjmcneill 			ci->ci_lpi[ci->ci_nlpi].reg_addr = addr.Address;
258*388bf930Sjmcneill 		}
259*388bf930Sjmcneill 
260*388bf930Sjmcneill 		if (lpi->Package.Elements[9].Type == ACPI_TYPE_STRING) {
261*388bf930Sjmcneill 			ci->ci_lpi[ci->ci_nlpi].name =
262*388bf930Sjmcneill 			    kmem_asprintf("LPI state %s",
263*388bf930Sjmcneill 				lpi->Package.Elements[9].String.Pointer);
264*388bf930Sjmcneill 		} else {
265*388bf930Sjmcneill 			ci->ci_lpi[ci->ci_nlpi].name =
266*388bf930Sjmcneill 			    kmem_asprintf("LPI state %u", n + 1);
267*388bf930Sjmcneill 		}
268*388bf930Sjmcneill 
269*388bf930Sjmcneill 		aprint_verbose_dev(ci->ci_dev,
270*388bf930Sjmcneill 		    "%s: min res %u, wakeup latency %u, flags %#x, "
271*388bf930Sjmcneill 		    "register %#x\n",
272*388bf930Sjmcneill 		    ci->ci_lpi[ci->ci_nlpi].name,
273*388bf930Sjmcneill 		    ci->ci_lpi[ci->ci_nlpi].min_res,
274*388bf930Sjmcneill 		    ci->ci_lpi[ci->ci_nlpi].wakeup_latency,
275*388bf930Sjmcneill 		    ci->ci_lpi[ci->ci_nlpi].save_restore_flags,
276*388bf930Sjmcneill 		    ci->ci_lpi[ci->ci_nlpi].reg_addr);
277*388bf930Sjmcneill 
278*388bf930Sjmcneill 		evcnt_attach_dynamic(&ci->ci_lpi[ci->ci_nlpi].events,
279*388bf930Sjmcneill 		    EVCNT_TYPE_MISC, NULL, ci->ci_cpuname,
280*388bf930Sjmcneill 		    ci->ci_lpi[ci->ci_nlpi].name);
281*388bf930Sjmcneill 
282*388bf930Sjmcneill 		ci->ci_nlpi++;
283*388bf930Sjmcneill 	}
284*388bf930Sjmcneill 
285*388bf930Sjmcneill 	if (ci->ci_nlpi > 0) {
286*388bf930Sjmcneill 		extern void (*arm_cpu_idle)(void);
287*388bf930Sjmcneill 		arm_cpu_idle = cpu_acpi_lpi_idle;
288*388bf930Sjmcneill 	}
289*388bf930Sjmcneill 
290*388bf930Sjmcneill out:
291*388bf930Sjmcneill 	ACPI_FREE(buf.Pointer);
292*388bf930Sjmcneill }
293*388bf930Sjmcneill 
294*388bf930Sjmcneill static inline void
295*388bf930Sjmcneill cpu_acpi_idle(uint32_t addr)
296*388bf930Sjmcneill {
297*388bf930Sjmcneill 	if (addr == LPI_REG_ADDR_WFI) {
298*388bf930Sjmcneill 		asm volatile("dsb sy; wfi");
299*388bf930Sjmcneill 	} else {
300*388bf930Sjmcneill 		psci_cpu_suspend(addr);
301*388bf930Sjmcneill 	}
302*388bf930Sjmcneill }
303*388bf930Sjmcneill 
304*388bf930Sjmcneill void
305*388bf930Sjmcneill cpu_acpi_lpi_idle(void)
306*388bf930Sjmcneill {
307*388bf930Sjmcneill 	struct cpu_info *ci = curcpu();
308*388bf930Sjmcneill 	struct timeval start, end;
309*388bf930Sjmcneill 	int n;
310*388bf930Sjmcneill 
311*388bf930Sjmcneill 	DISABLE_INTERRUPT();
312*388bf930Sjmcneill 
313*388bf930Sjmcneill 	microuptime(&start);
314*388bf930Sjmcneill 	for (n = ci->ci_nlpi - 1; n >= 0; n--) {
315*388bf930Sjmcneill 		if (ci->ci_last_idle >
316*388bf930Sjmcneill 		    LPI_IDLE_FACTOR * ci->ci_lpi[n].min_res) {
317*388bf930Sjmcneill 			cpu_acpi_idle(ci->ci_lpi[n].reg_addr);
318*388bf930Sjmcneill 			ci->ci_lpi[n].events.ev_count++;
319*388bf930Sjmcneill 			break;
320*388bf930Sjmcneill 		}
321*388bf930Sjmcneill 	}
322*388bf930Sjmcneill 	if (n == -1) {
323*388bf930Sjmcneill 		/* Nothing in _LPI, let's just WFI. */
324*388bf930Sjmcneill 		cpu_acpi_idle(LPI_REG_ADDR_WFI);
325*388bf930Sjmcneill 	}
326*388bf930Sjmcneill 	microuptime(&end);
327*388bf930Sjmcneill 	timersub(&end, &start, &end);
328*388bf930Sjmcneill 
329*388bf930Sjmcneill 	ci->ci_last_idle = end.tv_sec * 1000000 + end.tv_usec;
330*388bf930Sjmcneill 
331*388bf930Sjmcneill 	ENABLE_INTERRUPT();
332*388bf930Sjmcneill }
333*388bf930Sjmcneill 
334c3589be4Sjmcneill #if NTPROF > 0
335c3589be4Sjmcneill static struct cpu_info *
336c3589be4Sjmcneill cpu_acpi_find_processor(UINT32 uid)
337c3589be4Sjmcneill {
338c3589be4Sjmcneill 	CPU_INFO_ITERATOR cii;
339c3589be4Sjmcneill 	struct cpu_info *ci;
340c3589be4Sjmcneill 
341c3589be4Sjmcneill 	for (CPU_INFO_FOREACH(cii, ci)) {
342c3589be4Sjmcneill 		if (ci->ci_acpiid == uid)
343c3589be4Sjmcneill 			return ci;
344c3589be4Sjmcneill 	}
345c3589be4Sjmcneill 
346c3589be4Sjmcneill 	return NULL;
347c3589be4Sjmcneill }
348c3589be4Sjmcneill 
349c3589be4Sjmcneill static ACPI_STATUS
350c3589be4Sjmcneill cpu_acpi_tprof_intr_establish(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
351c3589be4Sjmcneill {
352c3589be4Sjmcneill 	device_t dev = aux;
353c3589be4Sjmcneill 	ACPI_MADT_GENERIC_INTERRUPT *gicc;
354c3589be4Sjmcneill 	struct cpu_info *ci;
355c3589be4Sjmcneill 	char xname[16];
356c3589be4Sjmcneill 	kcpuset_t *set;
357c3589be4Sjmcneill 	int error;
358c3589be4Sjmcneill 	void *ih;
359c3589be4Sjmcneill 
360c3589be4Sjmcneill 	if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_INTERRUPT)
361c3589be4Sjmcneill 		return AE_OK;
362c3589be4Sjmcneill 
363c3589be4Sjmcneill 	gicc = (ACPI_MADT_GENERIC_INTERRUPT *)hdrp;
364c3589be4Sjmcneill 	if ((gicc->Flags & ACPI_MADT_ENABLED) == 0)
365c3589be4Sjmcneill 		return AE_OK;
366c3589be4Sjmcneill 
3674d197dd7Sjmcneill 	const bool cpu_primary_p = cpu_info_store[0].ci_cpuid == gicc->ArmMpidr;
368c3589be4Sjmcneill 	const bool intr_ppi_p = gicc->PerformanceInterrupt < 32;
369999b9562Sjmcneill 	const int type = (gicc->Flags & ACPI_MADT_PERFORMANCE_IRQ_MODE) ?
370999b9562Sjmcneill 	    IST_EDGE : IST_LEVEL;
371c3589be4Sjmcneill 
372c3589be4Sjmcneill 	if (intr_ppi_p && !cpu_primary_p)
373c3589be4Sjmcneill 		return AE_OK;
374c3589be4Sjmcneill 
375c3589be4Sjmcneill 	ci = cpu_acpi_find_processor(gicc->Uid);
376c3589be4Sjmcneill 	if (ci == NULL) {
377999b9562Sjmcneill 		aprint_error_dev(dev, "couldn't find processor %#x\n",
378999b9562Sjmcneill 		    gicc->Uid);
379c3589be4Sjmcneill 		return AE_OK;
380c3589be4Sjmcneill 	}
381c3589be4Sjmcneill 
382c3589be4Sjmcneill 	if (intr_ppi_p) {
383c3589be4Sjmcneill 		strlcpy(xname, "pmu", sizeof(xname));
384c3589be4Sjmcneill 	} else {
385c3589be4Sjmcneill 		snprintf(xname, sizeof(xname), "pmu %s", cpu_name(ci));
386c3589be4Sjmcneill 	}
387c3589be4Sjmcneill 
388999b9562Sjmcneill 	ih = intr_establish_xname(gicc->PerformanceInterrupt, IPL_HIGH,
389999b9562Sjmcneill 	    type | IST_MPSAFE, armv8_pmu_intr, NULL, xname);
390c3589be4Sjmcneill 	if (ih == NULL) {
391999b9562Sjmcneill 		aprint_error_dev(dev, "couldn't establish %s interrupt\n",
392999b9562Sjmcneill 		    xname);
393c3589be4Sjmcneill 		return AE_OK;
394c3589be4Sjmcneill 	}
395c3589be4Sjmcneill 
396c3589be4Sjmcneill 	if (!intr_ppi_p) {
397c3589be4Sjmcneill 		kcpuset_create(&set, true);
398c3589be4Sjmcneill 		kcpuset_set(set, cpu_index(ci));
399c3589be4Sjmcneill 		error = interrupt_distribute(ih, set, NULL);
400c3589be4Sjmcneill 		kcpuset_destroy(set);
401c3589be4Sjmcneill 
402c3589be4Sjmcneill 		if (error) {
403999b9562Sjmcneill 			aprint_error_dev(dev,
404999b9562Sjmcneill 			    "failed to distribute %s interrupt: %d\n",
405c3589be4Sjmcneill 			    xname, error);
406c3589be4Sjmcneill 			return AE_OK;
407c3589be4Sjmcneill 		}
408c3589be4Sjmcneill 	}
409c3589be4Sjmcneill 
410999b9562Sjmcneill 	aprint_normal("%s: PMU interrupting on irq %d\n", cpu_name(ci),
411999b9562Sjmcneill 	    gicc->PerformanceInterrupt);
412c3589be4Sjmcneill 
413c3589be4Sjmcneill 	return AE_OK;
414c3589be4Sjmcneill }
415c3589be4Sjmcneill 
416c3589be4Sjmcneill static void
417c3589be4Sjmcneill cpu_acpi_tprof_init(device_t self)
418c3589be4Sjmcneill {
419367f8a70Sskrll 	int err = armv8_pmu_init();
420367f8a70Sskrll 	if (err) {
421367f8a70Sskrll 		aprint_error_dev(self,
422367f8a70Sskrll 		    "failed to initialize PMU event counter\n");
423367f8a70Sskrll 		return;
424367f8a70Sskrll 	}
425c3589be4Sjmcneill 
426c3589be4Sjmcneill 	if (acpi_madt_map() != AE_OK) {
427999b9562Sjmcneill 		aprint_error_dev(self,
428999b9562Sjmcneill 		    "failed to map MADT, performance counters not available\n");
429c3589be4Sjmcneill 		return;
430c3589be4Sjmcneill 	}
431c3589be4Sjmcneill 	acpi_madt_walk(cpu_acpi_tprof_intr_establish, self);
432c3589be4Sjmcneill 	acpi_madt_unmap();
433c3589be4Sjmcneill }
434c3589be4Sjmcneill #endif
435