1 /*-
2 * Copyright (c) 2015 Ruslan Bukin <[email protected]>
3 * All rights reserved.
4 *
5 * This software was developed by SRI International and the University of
6 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7 * ("CTSRD"), as part of the DARPA CRASH research programme.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/rman.h>
38
39 #include <dev/ofw/openfirm.h>
40 #include <dev/ofw/ofw_bus.h>
41 #include <dev/ofw/ofw_bus_subr.h>
42
43 #include "pmu.h"
44
45 static struct ofw_compat_data compat_data[] = {
46 {"arm,armv8-pmuv3", 1},
47 {"arm,cortex-a77-pmu", 1},
48 {"arm,cortex-a76-pmu", 1},
49 {"arm,cortex-a75-pmu", 1},
50 {"arm,cortex-a73-pmu", 1},
51 {"arm,cortex-a72-pmu", 1},
52 {"arm,cortex-a65-pmu", 1},
53 {"arm,cortex-a57-pmu", 1},
54 {"arm,cortex-a55-pmu", 1},
55 {"arm,cortex-a53-pmu", 1},
56 {"arm,cortex-a34-pmu", 1},
57
58 {"arm,cortex-a17-pmu", 1},
59 {"arm,cortex-a15-pmu", 1},
60 {"arm,cortex-a12-pmu", 1},
61 {"arm,cortex-a9-pmu", 1},
62 {"arm,cortex-a8-pmu", 1},
63 {"arm,cortex-a7-pmu", 1},
64 {"arm,cortex-a5-pmu", 1},
65 {"arm,arm11mpcore-pmu", 1},
66 {"arm,arm1176-pmu", 1},
67 {"arm,arm1136-pmu", 1},
68 {"qcom,krait-pmu", 1},
69 {NULL, 0}
70 };
71
72 static int
pmu_fdt_probe(device_t dev)73 pmu_fdt_probe(device_t dev)
74 {
75
76 if (!ofw_bus_status_okay(dev))
77 return (ENXIO);
78
79 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
80 device_set_desc(dev, "Performance Monitoring Unit");
81 return (BUS_PROBE_DEFAULT);
82 }
83
84 return (ENXIO);
85 }
86
87 static int
pmu_parse_affinity(device_t dev,struct pmu_softc * sc,struct pmu_intr * irq,phandle_t xref,uint32_t mpidr)88 pmu_parse_affinity(device_t dev, struct pmu_softc *sc, struct pmu_intr *irq,
89 phandle_t xref, uint32_t mpidr)
90 {
91 struct pcpu *pcpu;
92 int i, err;
93
94
95 if (xref != 0) {
96 err = OF_getencprop(OF_node_from_xref(xref), "reg", &mpidr,
97 sizeof(mpidr));
98 if (err < 0) {
99 device_printf(dev, "missing 'reg' property\n");
100 return (ENXIO);
101 }
102 }
103
104 for (i = 0; i < MAXCPU; i++) {
105 pcpu = pcpu_find(i);
106 if (pcpu != NULL && PCPU_GET_MPIDR(pcpu) == mpidr) {
107 irq->cpuid = i;
108 return (0);
109 }
110 }
111
112 device_printf(dev, "Cannot find CPU with MPIDR: 0x%08X\n", mpidr);
113 return (ENXIO);
114 }
115
116 static int
pmu_parse_intr(device_t dev,struct pmu_softc * sc)117 pmu_parse_intr(device_t dev, struct pmu_softc *sc)
118 {
119 bool has_affinity;
120 phandle_t node, *cpus;
121 int rid, err, ncpus, i;
122
123
124 node = ofw_bus_get_node(dev);
125 has_affinity = OF_hasprop(node, "interrupt-affinity");
126
127 for (i = 0; i < MAX_RLEN; i++)
128 sc->irq[i].cpuid = -1;
129
130 cpus = NULL;
131 if (has_affinity) {
132 ncpus = OF_getencprop_alloc_multi(node, "interrupt-affinity",
133 sizeof(*cpus), (void **)&cpus);
134 if (ncpus < 0) {
135 device_printf(dev,
136 "Cannot read interrupt affinity property\n");
137 return (ENXIO);
138 }
139 }
140
141 /* Process first interrupt */
142 rid = 0;
143 sc->irq[0].res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
144 RF_ACTIVE | RF_SHAREABLE);
145
146 if (sc->irq[0].res == NULL) {
147 device_printf(dev, "Cannot get interrupt\n");
148 err = ENXIO;
149 goto done;
150 }
151
152 /* Check if PMU have one per-CPU interrupt */
153 if (intr_is_per_cpu(sc->irq[0].res)) {
154 if (has_affinity) {
155 device_printf(dev,
156 "Per CPU interupt have declared affinity\n");
157 err = ENXIO;
158 goto done;
159 }
160 return (0);
161 }
162
163 /*
164 * PMU with set of generic interrupts (one per core)
165 * Each one must be binded to exact core.
166 */
167 err = pmu_parse_affinity(dev, sc, sc->irq + 0,
168 has_affinity ? cpus[0] : 0, 0);
169 if (err != 0) {
170 device_printf(dev, "Cannot parse affinity for CPUid: 0\n");
171 goto done;
172 }
173
174 for (i = 1; i < MAX_RLEN; i++) {
175 rid = i;
176 sc->irq[i].res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
177 &rid, RF_ACTIVE | RF_SHAREABLE);
178 if (sc->irq[i].res == NULL)
179 break;
180
181 if (intr_is_per_cpu(sc->irq[i].res))
182 {
183 device_printf(dev, "Unexpected per CPU interupt\n");
184 err = ENXIO;
185 goto done;
186 }
187
188 if (has_affinity && i >= ncpus) {
189 device_printf(dev, "Missing value in interrupt "
190 "affinity property\n");
191 err = ENXIO;
192 goto done;
193 }
194
195 err = pmu_parse_affinity(dev, sc, sc->irq + i,
196 has_affinity ? cpus[i] : 0, i);
197 if (err != 0) {
198 device_printf(dev,
199 "Cannot parse affinity for CPUid: %d.\n", i);
200 goto done;
201 }
202 }
203 err = 0;
204 done:
205 OF_prop_free(cpus);
206 return (err);
207 }
208
209 static int
pmu_fdt_attach(device_t dev)210 pmu_fdt_attach(device_t dev)
211 {
212 struct pmu_softc *sc;
213 int err;
214
215 sc = device_get_softc(dev);
216 err = pmu_parse_intr(dev, sc);
217 if (err != 0)
218 return (err);
219
220 return (pmu_attach(dev));
221 }
222
223 static device_method_t pmu_fdt_methods[] = {
224 DEVMETHOD(device_probe, pmu_fdt_probe),
225 DEVMETHOD(device_attach, pmu_fdt_attach),
226 { 0, 0 }
227 };
228
229 static driver_t pmu_fdt_driver = {
230 "pmu",
231 pmu_fdt_methods,
232 sizeof(struct pmu_softc),
233 };
234
235 DRIVER_MODULE(pmu, simplebus, pmu_fdt_driver, 0, 0);
236