cpu_acpi.c revision 1.6.2.2 1 1.6.2.2 christos /* $NetBSD: cpu_acpi.c,v 1.6.2.2 2019/06/10 22:05:50 christos Exp $ */
2 1.6.2.2 christos
3 1.6.2.2 christos /*-
4 1.6.2.2 christos * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 1.6.2.2 christos * All rights reserved.
6 1.6.2.2 christos *
7 1.6.2.2 christos * This code is derived from software contributed to The NetBSD Foundation
8 1.6.2.2 christos * by Jared McNeill <jmcneill (at) invisible.ca>.
9 1.6.2.2 christos *
10 1.6.2.2 christos * Redistribution and use in source and binary forms, with or without
11 1.6.2.2 christos * modification, are permitted provided that the following conditions
12 1.6.2.2 christos * are met:
13 1.6.2.2 christos * 1. Redistributions of source code must retain the above copyright
14 1.6.2.2 christos * notice, this list of conditions and the following disclaimer.
15 1.6.2.2 christos * 2. Redistributions in binary form must reproduce the above copyright
16 1.6.2.2 christos * notice, this list of conditions and the following disclaimer in the
17 1.6.2.2 christos * documentation and/or other materials provided with the distribution.
18 1.6.2.2 christos *
19 1.6.2.2 christos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.6.2.2 christos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.6.2.2 christos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.6.2.2 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.6.2.2 christos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.6.2.2 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.6.2.2 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.6.2.2 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.6.2.2 christos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.6.2.2 christos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.6.2.2 christos * POSSIBILITY OF SUCH DAMAGE.
30 1.6.2.2 christos */
31 1.6.2.2 christos
32 1.6.2.2 christos #include "tprof.h"
33 1.6.2.2 christos #include "opt_multiprocessor.h"
34 1.6.2.2 christos
35 1.6.2.2 christos #include <sys/cdefs.h>
36 1.6.2.2 christos __KERNEL_RCSID(0, "$NetBSD: cpu_acpi.c,v 1.6.2.2 2019/06/10 22:05:50 christos Exp $");
37 1.6.2.2 christos
38 1.6.2.2 christos #include <sys/param.h>
39 1.6.2.2 christos #include <sys/bus.h>
40 1.6.2.2 christos #include <sys/cpu.h>
41 1.6.2.2 christos #include <sys/device.h>
42 1.6.2.2 christos #include <sys/interrupt.h>
43 1.6.2.2 christos #include <sys/kcpuset.h>
44 1.6.2.2 christos
45 1.6.2.2 christos #include <dev/acpi/acpireg.h>
46 1.6.2.2 christos #include <dev/acpi/acpivar.h>
47 1.6.2.2 christos
48 1.6.2.2 christos #include <arm/armreg.h>
49 1.6.2.2 christos #include <arm/cpu.h>
50 1.6.2.2 christos #include <arm/cpufunc.h>
51 1.6.2.2 christos #include <arm/locore.h>
52 1.6.2.2 christos
53 1.6.2.2 christos #include <arm/arm/psci.h>
54 1.6.2.2 christos
55 1.6.2.2 christos #if NTPROF > 0
56 1.6.2.2 christos #include <dev/tprof/tprof_armv8.h>
57 1.6.2.2 christos #endif
58 1.6.2.2 christos
59 1.6.2.2 christos extern struct cpu_info cpu_info_store[];
60 1.6.2.2 christos
61 1.6.2.2 christos static int cpu_acpi_match(device_t, cfdata_t, void *);
62 1.6.2.2 christos static void cpu_acpi_attach(device_t, device_t, void *);
63 1.6.2.2 christos
64 1.6.2.2 christos #if NTPROF > 0
65 1.6.2.2 christos static void cpu_acpi_tprof_init(device_t);
66 1.6.2.2 christos #endif
67 1.6.2.2 christos
68 1.6.2.2 christos CFATTACH_DECL_NEW(cpu_acpi, 0, cpu_acpi_match, cpu_acpi_attach, NULL, NULL);
69 1.6.2.2 christos
70 1.6.2.2 christos #ifdef MULTIPROCESSOR
71 1.6.2.2 christos static register_t
72 1.6.2.2 christos cpu_acpi_mpstart_pa(void)
73 1.6.2.2 christos {
74 1.6.2.2 christos
75 1.6.2.2 christos return (register_t)KERN_VTOPHYS((vaddr_t)cpu_mpstart);
76 1.6.2.2 christos }
77 1.6.2.2 christos #endif /* MULTIPROCESSOR */
78 1.6.2.2 christos
79 1.6.2.2 christos static int
80 1.6.2.2 christos cpu_acpi_match(device_t parent, cfdata_t cf, void *aux)
81 1.6.2.2 christos {
82 1.6.2.2 christos ACPI_SUBTABLE_HEADER *hdrp = aux;
83 1.6.2.2 christos ACPI_MADT_GENERIC_INTERRUPT *gicc;
84 1.6.2.2 christos
85 1.6.2.2 christos if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_INTERRUPT)
86 1.6.2.2 christos return 0;
87 1.6.2.2 christos
88 1.6.2.2 christos gicc = (ACPI_MADT_GENERIC_INTERRUPT *)hdrp;
89 1.6.2.2 christos
90 1.6.2.2 christos return (gicc->Flags & ACPI_MADT_ENABLED) != 0;
91 1.6.2.2 christos }
92 1.6.2.2 christos
93 1.6.2.2 christos static void
94 1.6.2.2 christos cpu_acpi_attach(device_t parent, device_t self, void *aux)
95 1.6.2.2 christos {
96 1.6.2.2 christos ACPI_MADT_GENERIC_INTERRUPT *gicc = aux;
97 1.6.2.2 christos const uint64_t mpidr = gicc->ArmMpidr;
98 1.6.2.2 christos const int unit = device_unit(self);
99 1.6.2.2 christos struct cpu_info *ci = &cpu_info_store[unit];
100 1.6.2.2 christos
101 1.6.2.2 christos #ifdef MULTIPROCESSOR
102 1.6.2.2 christos if (cpu_mpidr_aff_read() != mpidr) {
103 1.6.2.2 christos const u_int cpuindex = device_unit(self);
104 1.6.2.2 christos int error;
105 1.6.2.2 christos
106 1.6.2.2 christos cpu_mpidr[cpuindex] = mpidr;
107 1.6.2.2 christos cpu_dcache_wb_range((vaddr_t)&cpu_mpidr[cpuindex], sizeof(cpu_mpidr[cpuindex]));
108 1.6.2.2 christos
109 1.6.2.2 christos /* XXX support spin table */
110 1.6.2.2 christos error = psci_cpu_on(mpidr, cpu_acpi_mpstart_pa(), 0);
111 1.6.2.2 christos if (error != PSCI_SUCCESS) {
112 1.6.2.2 christos aprint_error_dev(self, "failed to start CPU\n");
113 1.6.2.2 christos return;
114 1.6.2.2 christos }
115 1.6.2.2 christos
116 1.6.2.2 christos __asm __volatile("sev" ::: "memory");
117 1.6.2.2 christos
118 1.6.2.2 christos for (u_int i = 0x10000000; i > 0; i--) {
119 1.6.2.2 christos membar_consumer();
120 1.6.2.2 christos if (arm_cpu_hatched & __BIT(cpuindex))
121 1.6.2.2 christos break;
122 1.6.2.2 christos }
123 1.6.2.2 christos }
124 1.6.2.2 christos #endif /* MULTIPROCESSOR */
125 1.6.2.2 christos
126 1.6.2.2 christos /* Store the ACPI Processor UID in cpu_info */
127 1.6.2.2 christos ci->ci_acpiid = gicc->Uid;
128 1.6.2.2 christos
129 1.6.2.2 christos /* Attach the CPU */
130 1.6.2.2 christos cpu_attach(self, mpidr);
131 1.6.2.2 christos
132 1.6.2.2 christos #if NTPROF > 0
133 1.6.2.2 christos if (cpu_mpidr_aff_read() == mpidr)
134 1.6.2.2 christos config_interrupts(self, cpu_acpi_tprof_init);
135 1.6.2.2 christos #endif
136 1.6.2.2 christos }
137 1.6.2.2 christos
138 1.6.2.2 christos #if NTPROF > 0
139 1.6.2.2 christos static struct cpu_info *
140 1.6.2.2 christos cpu_acpi_find_processor(UINT32 uid)
141 1.6.2.2 christos {
142 1.6.2.2 christos CPU_INFO_ITERATOR cii;
143 1.6.2.2 christos struct cpu_info *ci;
144 1.6.2.2 christos
145 1.6.2.2 christos for (CPU_INFO_FOREACH(cii, ci)) {
146 1.6.2.2 christos if (ci->ci_acpiid == uid)
147 1.6.2.2 christos return ci;
148 1.6.2.2 christos }
149 1.6.2.2 christos
150 1.6.2.2 christos return NULL;
151 1.6.2.2 christos }
152 1.6.2.2 christos
153 1.6.2.2 christos static ACPI_STATUS
154 1.6.2.2 christos cpu_acpi_tprof_intr_establish(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
155 1.6.2.2 christos {
156 1.6.2.2 christos device_t dev = aux;
157 1.6.2.2 christos ACPI_MADT_GENERIC_INTERRUPT *gicc;
158 1.6.2.2 christos struct cpu_info *ci;
159 1.6.2.2 christos char xname[16];
160 1.6.2.2 christos kcpuset_t *set;
161 1.6.2.2 christos int error;
162 1.6.2.2 christos void *ih;
163 1.6.2.2 christos
164 1.6.2.2 christos if (hdrp->Type != ACPI_MADT_TYPE_GENERIC_INTERRUPT)
165 1.6.2.2 christos return AE_OK;
166 1.6.2.2 christos
167 1.6.2.2 christos gicc = (ACPI_MADT_GENERIC_INTERRUPT *)hdrp;
168 1.6.2.2 christos if ((gicc->Flags & ACPI_MADT_ENABLED) == 0)
169 1.6.2.2 christos return AE_OK;
170 1.6.2.2 christos
171 1.6.2.2 christos const bool cpu_primary_p = cpu_mpidr_aff_read() == gicc->ArmMpidr;
172 1.6.2.2 christos const bool intr_ppi_p = gicc->PerformanceInterrupt < 32;
173 1.6.2.2 christos const int type = (gicc->Flags & ACPI_MADT_PERFORMANCE_IRQ_MODE) ? IST_EDGE : IST_LEVEL;
174 1.6.2.2 christos
175 1.6.2.2 christos if (intr_ppi_p && !cpu_primary_p)
176 1.6.2.2 christos return AE_OK;
177 1.6.2.2 christos
178 1.6.2.2 christos ci = cpu_acpi_find_processor(gicc->Uid);
179 1.6.2.2 christos if (ci == NULL) {
180 1.6.2.2 christos aprint_error_dev(dev, "couldn't find processor %#x\n", gicc->Uid);
181 1.6.2.2 christos return AE_OK;
182 1.6.2.2 christos }
183 1.6.2.2 christos
184 1.6.2.2 christos if (intr_ppi_p) {
185 1.6.2.2 christos strlcpy(xname, "pmu", sizeof(xname));
186 1.6.2.2 christos } else {
187 1.6.2.2 christos snprintf(xname, sizeof(xname), "pmu %s", cpu_name(ci));
188 1.6.2.2 christos }
189 1.6.2.2 christos
190 1.6.2.2 christos ih = intr_establish_xname(gicc->PerformanceInterrupt, IPL_HIGH, type | IST_MPSAFE,
191 1.6.2.2 christos armv8_pmu_intr, NULL, xname);
192 1.6.2.2 christos if (ih == NULL) {
193 1.6.2.2 christos aprint_error_dev(dev, "couldn't establish %s interrupt\n", xname);
194 1.6.2.2 christos return AE_OK;
195 1.6.2.2 christos }
196 1.6.2.2 christos
197 1.6.2.2 christos if (!intr_ppi_p) {
198 1.6.2.2 christos kcpuset_create(&set, true);
199 1.6.2.2 christos kcpuset_set(set, cpu_index(ci));
200 1.6.2.2 christos error = interrupt_distribute(ih, set, NULL);
201 1.6.2.2 christos kcpuset_destroy(set);
202 1.6.2.2 christos
203 1.6.2.2 christos if (error) {
204 1.6.2.2 christos aprint_error_dev(dev, "failed to distribute %s interrupt: %d\n",
205 1.6.2.2 christos xname, error);
206 1.6.2.2 christos return AE_OK;
207 1.6.2.2 christos }
208 1.6.2.2 christos }
209 1.6.2.2 christos
210 1.6.2.2 christos aprint_normal("%s: PMU interrupting on irq %d\n", cpu_name(ci), gicc->PerformanceInterrupt);
211 1.6.2.2 christos
212 1.6.2.2 christos return AE_OK;
213 1.6.2.2 christos }
214 1.6.2.2 christos
215 1.6.2.2 christos static void
216 1.6.2.2 christos cpu_acpi_tprof_init(device_t self)
217 1.6.2.2 christos {
218 1.6.2.2 christos armv8_pmu_init();
219 1.6.2.2 christos
220 1.6.2.2 christos if (acpi_madt_map() != AE_OK) {
221 1.6.2.2 christos aprint_error_dev(self, "failed to map MADT, performance counters not available\n");
222 1.6.2.2 christos return;
223 1.6.2.2 christos }
224 1.6.2.2 christos acpi_madt_walk(cpu_acpi_tprof_intr_establish, self);
225 1.6.2.2 christos acpi_madt_unmap();
226 1.6.2.2 christos }
227 1.6.2.2 christos #endif
228