xref: /freebsd/sys/arm/arm/pmu_fdt.c (revision 069ac184)
1 /*-
2  * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
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/param.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/rman.h>
37 
38 #include <dev/ofw/openfirm.h>
39 #include <dev/ofw/ofw_bus.h>
40 #include <dev/ofw/ofw_bus_subr.h>
41 
42 #include "pmu.h"
43 
44 static struct ofw_compat_data compat_data[] = {
45 	{"arm,armv8-pmuv3",	1},
46 	{"arm,cortex-a77-pmu",  1},
47 	{"arm,cortex-a76-pmu",  1},
48 	{"arm,cortex-a75-pmu",  1},
49 	{"arm,cortex-a73-pmu",  1},
50 	{"arm,cortex-a72-pmu",  1},
51 	{"arm,cortex-a65-pmu",  1},
52 	{"arm,cortex-a57-pmu",  1},
53 	{"arm,cortex-a55-pmu",  1},
54 	{"arm,cortex-a53-pmu",  1},
55 	{"arm,cortex-a34-pmu",  1},
56 
57 	{"arm,cortex-a17-pmu",	1},
58 	{"arm,cortex-a15-pmu",	1},
59 	{"arm,cortex-a12-pmu",	1},
60 	{"arm,cortex-a9-pmu",	1},
61 	{"arm,cortex-a8-pmu",	1},
62 	{"arm,cortex-a7-pmu",	1},
63 	{"arm,cortex-a5-pmu",	1},
64 	{"arm,arm11mpcore-pmu",	1},
65 	{"arm,arm1176-pmu",	1},
66 	{"arm,arm1136-pmu",	1},
67 	{"qcom,krait-pmu",	1},
68 	{NULL,			0}
69 };
70 
71 static int
72 pmu_fdt_probe(device_t dev)
73 {
74 
75 	if (!ofw_bus_status_okay(dev))
76 		return (ENXIO);
77 
78 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
79 		device_set_desc(dev, "Performance Monitoring Unit");
80 		return (BUS_PROBE_DEFAULT);
81 	}
82 
83 	return (ENXIO);
84 }
85 
86 static int
87 pmu_parse_affinity(device_t dev, struct pmu_softc *sc, struct pmu_intr *irq,
88     phandle_t xref, uint32_t mpidr)
89 {
90 	struct pcpu *pcpu;
91 	int i, err;
92 
93 
94 	if (xref  != 0) {
95 		err = OF_getencprop(OF_node_from_xref(xref), "reg", &mpidr,
96 		    sizeof(mpidr));
97 		if (err < 0) {
98 			device_printf(dev, "missing 'reg' property\n");
99 				return (ENXIO);
100 		}
101 	}
102 
103 	for (i = 0; i < MAXCPU; i++) {
104 		pcpu = pcpu_find(i);
105 		if (pcpu != NULL && PCPU_GET_MPIDR(pcpu) == mpidr) {
106 			irq->cpuid = i;
107 			return (0);
108 		}
109 	}
110 
111 	device_printf(dev, "Cannot find CPU with MPIDR: 0x%08X\n", mpidr);
112 	return (ENXIO);
113 }
114 
115 static int
116 pmu_parse_intr(device_t dev, struct pmu_softc *sc)
117 {
118 	bool has_affinity;
119 	phandle_t node, *cpus;
120 	int rid, err, ncpus, i;
121 
122 
123 	node = ofw_bus_get_node(dev);
124 	has_affinity = OF_hasprop(node, "interrupt-affinity");
125 
126 	for (i = 0; i < MAX_RLEN; i++)
127 		sc->irq[i].cpuid = -1;
128 
129 	cpus = NULL;
130 	if (has_affinity) {
131 		ncpus = OF_getencprop_alloc_multi(node, "interrupt-affinity",
132 		    sizeof(*cpus), (void **)&cpus);
133 		if (ncpus < 0) {
134 			device_printf(dev,
135 			    "Cannot read interrupt affinity property\n");
136 			return (ENXIO);
137 		}
138 	}
139 
140 	/* Process first interrupt */
141 	rid = 0;
142 	sc->irq[0].res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
143 	    RF_ACTIVE | RF_SHAREABLE);
144 
145 	if (sc->irq[0].res == NULL) {
146 		device_printf(dev, "Cannot get interrupt\n");
147 		err = ENXIO;
148 		goto done;
149 	}
150 
151 	/* Check if PMU have one per-CPU interrupt */
152 	if (intr_is_per_cpu(sc->irq[0].res)) {
153 		if (has_affinity) {
154 			device_printf(dev,
155 			    "Per CPU interupt have declared affinity\n");
156 			err = ENXIO;
157 			goto done;
158 		}
159 		return (0);
160 	}
161 
162 	/*
163 	 * PMU with set of generic interrupts (one per core)
164 	 * Each one must be binded to exact core.
165 	 */
166 	err = pmu_parse_affinity(dev, sc, sc->irq + 0,
167 	    has_affinity ? cpus[0] : 0, 0);
168 	if (err != 0) {
169 		device_printf(dev, "Cannot parse affinity for CPUid: 0\n");
170 		goto done;
171 	}
172 
173 	for (i = 1; i < MAX_RLEN; i++) {
174 		rid = i;
175 		sc->irq[i].res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
176 		    &rid, RF_ACTIVE | RF_SHAREABLE);
177 		if (sc->irq[i].res == NULL)
178 			break;
179 
180 		if (intr_is_per_cpu(sc->irq[i].res))
181 		{
182 			device_printf(dev, "Unexpected per CPU interupt\n");
183 			err = ENXIO;
184 			goto done;
185 		}
186 
187 		if (has_affinity && i >= ncpus) {
188 			device_printf(dev, "Missing value in interrupt "
189 			    "affinity property\n");
190 			err = ENXIO;
191 			goto done;
192 		}
193 
194 		err = pmu_parse_affinity(dev, sc, sc->irq + i,
195 		    has_affinity ? cpus[i] : 0, i);
196 		if (err != 0) {
197 			device_printf(dev,
198 			   "Cannot parse affinity for CPUid: %d.\n", i);
199 			goto done;
200 		}
201 	}
202 	err = 0;
203 done:
204 	OF_prop_free(cpus);
205 	return (err);
206 }
207 
208 static int
209 pmu_fdt_attach(device_t dev)
210 {
211 	struct pmu_softc *sc;
212 	int err;
213 
214 	sc = device_get_softc(dev);
215 	err = pmu_parse_intr(dev, sc);
216 	if (err != 0)
217 		return (err);
218 
219 	return (pmu_attach(dev));
220 }
221 
222 static device_method_t pmu_fdt_methods[] = {
223 	DEVMETHOD(device_probe,		pmu_fdt_probe),
224 	DEVMETHOD(device_attach,	pmu_fdt_attach),
225 	{ 0, 0 }
226 };
227 
228 static driver_t pmu_fdt_driver = {
229 	"pmu",
230 	pmu_fdt_methods,
231 	sizeof(struct pmu_softc),
232 };
233 
234 DRIVER_MODULE(pmu, simplebus, pmu_fdt_driver, 0, 0);
235