1277745Sbr/*-
2277745Sbr * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
3277745Sbr * All rights reserved.
4277745Sbr *
5277745Sbr * This software was developed by SRI International and the University of
6277745Sbr * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7277745Sbr * ("CTSRD"), as part of the DARPA CRASH research programme.
8277745Sbr *
9277745Sbr * Redistribution and use in source and binary forms, with or without
10277745Sbr * modification, are permitted provided that the following conditions
11277745Sbr * are met:
12277745Sbr * 1. Redistributions of source code must retain the above copyright
13277745Sbr *    notice, this list of conditions and the following disclaimer.
14277745Sbr * 2. Redistributions in binary form must reproduce the above copyright
15277745Sbr *    notice, this list of conditions and the following disclaimer in the
16277745Sbr *    documentation and/or other materials provided with the distribution.
17277745Sbr *
18277745Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19277745Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20277745Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21277745Sbr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22277745Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23277745Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24277745Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25277745Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26277745Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27277745Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28277745Sbr * SUCH DAMAGE.
29277745Sbr */
30277745Sbr
31277745Sbr/*
32277745Sbr * Performance Monitoring Unit
33277745Sbr */
34277745Sbr
35277745Sbr#include <sys/cdefs.h>
36277745Sbr__FBSDID("$FreeBSD$");
37277745Sbr
38277745Sbr#include "opt_hwpmc_hooks.h"
39277745Sbr
40277745Sbr#include <sys/param.h>
41277745Sbr#include <sys/systm.h>
42277745Sbr#include <sys/bus.h>
43277745Sbr#include <sys/kernel.h>
44277745Sbr#include <sys/module.h>
45277745Sbr#include <sys/malloc.h>
46277745Sbr#include <sys/rman.h>
47277745Sbr#include <sys/timeet.h>
48277745Sbr#include <sys/timetc.h>
49277745Sbr#include <sys/pmc.h>
50277745Sbr#include <sys/pmckern.h>
51277745Sbr
52277745Sbr#include <dev/fdt/fdt_common.h>
53277745Sbr#include <dev/ofw/openfirm.h>
54277745Sbr#include <dev/ofw/ofw_bus.h>
55277745Sbr#include <dev/ofw/ofw_bus_subr.h>
56277745Sbr
57277745Sbr#include <machine/bus.h>
58277745Sbr#include <machine/cpu.h>
59277745Sbr#include <machine/intr.h>
60277745Sbr
61291210Sandrew#ifdef notyet
62283112Sbr#define	MAX_RLEN	8
63291210Sandrew#else
64291210Sandrew#define	MAX_RLEN	1
65291210Sandrew#endif
66283112Sbr
67277745Sbrstruct pmu_softc {
68283112Sbr	struct resource		*res[MAX_RLEN];
69277745Sbr	device_t		dev;
70283112Sbr	void			*ih[MAX_RLEN];
71277745Sbr};
72277745Sbr
73277745Sbrstatic struct ofw_compat_data compat_data[] = {
74283112Sbr	{"arm,armv8-pmuv3",	1},
75277745Sbr	{"arm,cortex-a17-pmu",	1},
76277745Sbr	{"arm,cortex-a15-pmu",	1},
77277745Sbr	{"arm,cortex-a12-pmu",	1},
78277745Sbr	{"arm,cortex-a9-pmu",	1},
79277745Sbr	{"arm,cortex-a8-pmu",	1},
80277745Sbr	{"arm,cortex-a7-pmu",	1},
81277745Sbr	{"arm,cortex-a5-pmu",	1},
82277745Sbr	{"arm,arm11mpcore-pmu",	1},
83277745Sbr	{"arm,arm1176-pmu",	1},
84277745Sbr	{"arm,arm1136-pmu",	1},
85277745Sbr	{"qcom,krait-pmu",	1},
86277745Sbr	{NULL,			0}
87277745Sbr};
88277745Sbr
89277745Sbrstatic struct resource_spec pmu_spec[] = {
90277745Sbr	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
91291210Sandrew	/* We don't currently handle pmu events, other than on cpu 0 */
92291216Sandrew#ifdef notyet
93283112Sbr	{ SYS_RES_IRQ,		1,	RF_ACTIVE | RF_OPTIONAL },
94283112Sbr	{ SYS_RES_IRQ,		2,	RF_ACTIVE | RF_OPTIONAL },
95283112Sbr	{ SYS_RES_IRQ,		3,	RF_ACTIVE | RF_OPTIONAL },
96283112Sbr	{ SYS_RES_IRQ,		4,	RF_ACTIVE | RF_OPTIONAL },
97283112Sbr	{ SYS_RES_IRQ,		5,	RF_ACTIVE | RF_OPTIONAL },
98283112Sbr	{ SYS_RES_IRQ,		6,	RF_ACTIVE | RF_OPTIONAL },
99283112Sbr	{ SYS_RES_IRQ,		7,	RF_ACTIVE | RF_OPTIONAL },
100291210Sandrew#endif
101277745Sbr	{ -1, 0 }
102277745Sbr};
103277745Sbr
104290614Sbz/* CCNT */
105290614Sbz#if __ARM_ARCH > 6
106290614Sbzint pmu_attched = 0;
107290614Sbzuint32_t ccnt_hi[MAXCPU];
108290614Sbz#endif
109290614Sbz
110290614Sbz#define	PMU_OVSR_C		0x80000000	/* Cycle Counter */
111290614Sbz#define	PMU_IESR_C		0x80000000	/* Cycle Counter */
112290614Sbz
113277745Sbrstatic int
114277745Sbrpmu_intr(void *arg)
115277745Sbr{
116290614Sbz#ifdef HWPMC_HOOKS
117277745Sbr	struct trapframe *tf;
118290614Sbz#endif
119290614Sbz	uint32_t r;
120290614Sbz#if defined(__arm__) && (__ARM_ARCH > 6)
121290614Sbz	u_int cpu;
122277745Sbr
123290614Sbz	cpu = PCPU_GET(cpuid);
124277745Sbr
125290614Sbz	r = cp15_pmovsr_get();
126290614Sbz	if (r & PMU_OVSR_C) {
127290614Sbz		atomic_add_32(&ccnt_hi[cpu], 1);
128290614Sbz		/* Clear the event. */
129290614Sbz		r &= ~PMU_OVSR_C;
130290614Sbz		cp15_pmovsr_set(PMU_OVSR_C);
131290614Sbz	}
132290614Sbz#else
133290614Sbz	r = 1;
134290614Sbz#endif
135290614Sbz
136277745Sbr#ifdef HWPMC_HOOKS
137290614Sbz	/* Only call into the HWPMC framework if we know there is work. */
138290614Sbz	if (r != 0 && pmc_intr) {
139290614Sbz		tf = arg;
140277745Sbr		(*pmc_intr)(PCPU_GET(cpuid), tf);
141290614Sbz	}
142277745Sbr#endif
143277745Sbr
144277745Sbr	return (FILTER_HANDLED);
145277745Sbr}
146277745Sbr
147277745Sbrstatic int
148277745Sbrpmu_probe(device_t dev)
149277745Sbr{
150277745Sbr
151277745Sbr	if (!ofw_bus_status_okay(dev))
152277745Sbr		return (ENXIO);
153277745Sbr
154277745Sbr	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
155277745Sbr		device_set_desc(dev, "Performance Monitoring Unit");
156277745Sbr		return (BUS_PROBE_DEFAULT);
157277745Sbr	}
158277745Sbr
159277745Sbr	return (ENXIO);
160277745Sbr}
161277745Sbr
162277745Sbrstatic int
163277745Sbrpmu_attach(device_t dev)
164277745Sbr{
165277745Sbr	struct pmu_softc *sc;
166290614Sbz#if defined(__arm__) && (__ARM_ARCH > 6)
167290614Sbz	uint32_t iesr;
168290614Sbz#endif
169277745Sbr	int err;
170283112Sbr	int i;
171277745Sbr
172277745Sbr	sc = device_get_softc(dev);
173277745Sbr	sc->dev = dev;
174277745Sbr
175277745Sbr	if (bus_alloc_resources(dev, pmu_spec, sc->res)) {
176277745Sbr		device_printf(dev, "could not allocate resources\n");
177277745Sbr		return (ENXIO);
178277745Sbr	}
179277745Sbr
180277745Sbr	/* Setup interrupt handler */
181283112Sbr	for (i = 0; i < MAX_RLEN; i++) {
182283112Sbr		if (sc->res[i] == NULL)
183283112Sbr			break;
184283112Sbr
185283112Sbr		err = bus_setup_intr(dev, sc->res[i], INTR_MPSAFE | INTR_TYPE_MISC,
186283112Sbr		    pmu_intr, NULL, NULL, &sc->ih[i]);
187283112Sbr		if (err) {
188283112Sbr			device_printf(dev, "Unable to setup interrupt handler.\n");
189283112Sbr			return (ENXIO);
190283112Sbr		}
191277745Sbr	}
192277745Sbr
193290614Sbz#if defined(__arm__) && (__ARM_ARCH > 6)
194290614Sbz	/* Initialize to 0. */
195290614Sbz	for (i = 0; i < MAXCPU; i++)
196290614Sbz		ccnt_hi[i] = 0;
197290614Sbz
198290614Sbz	/* Enable the interrupt to fire on overflow. */
199290614Sbz	iesr = cp15_pminten_get();
200290614Sbz	iesr |= PMU_IESR_C;
201290614Sbz	cp15_pminten_set(iesr);
202290614Sbz
203290614Sbz	/* Need this for getcyclecount() fast path. */
204290614Sbz	pmu_attched |= 1;
205290614Sbz#endif
206290614Sbz
207277745Sbr	return (0);
208277745Sbr}
209277745Sbr
210277745Sbrstatic device_method_t pmu_methods[] = {
211277745Sbr	DEVMETHOD(device_probe,		pmu_probe),
212277745Sbr	DEVMETHOD(device_attach,	pmu_attach),
213277745Sbr	{ 0, 0 }
214277745Sbr};
215277745Sbr
216277745Sbrstatic driver_t pmu_driver = {
217277745Sbr	"pmu",
218277745Sbr	pmu_methods,
219277745Sbr	sizeof(struct pmu_softc),
220277745Sbr};
221277745Sbr
222277745Sbrstatic devclass_t pmu_devclass;
223277745Sbr
224277745SbrDRIVER_MODULE(pmu, simplebus, pmu_driver, pmu_devclass, 0, 0);
225