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/*
32 * Performance Monitoring Unit
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38#include "opt_hwpmc_hooks.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/bus.h>
43#include <sys/kernel.h>
44#include <sys/module.h>
45#include <sys/malloc.h>
46#include <sys/rman.h>
47#include <sys/timeet.h>
48#include <sys/timetc.h>
49#include <sys/pmc.h>
50#include <sys/pmckern.h>
51
52#include <dev/fdt/fdt_common.h>
53#include <dev/ofw/openfirm.h>
54#include <dev/ofw/ofw_bus.h>
55#include <dev/ofw/ofw_bus_subr.h>
56
57#include <machine/bus.h>
58#include <machine/cpu.h>
59#include <machine/intr.h>
60
61#ifdef notyet
62#define	MAX_RLEN	8
63#else
64#define	MAX_RLEN	1
65#endif
66
67struct pmu_softc {
68	struct resource		*res[MAX_RLEN];
69	device_t		dev;
70	void			*ih[MAX_RLEN];
71};
72
73static struct ofw_compat_data compat_data[] = {
74	{"arm,armv8-pmuv3",	1},
75	{"arm,cortex-a17-pmu",	1},
76	{"arm,cortex-a15-pmu",	1},
77	{"arm,cortex-a12-pmu",	1},
78	{"arm,cortex-a9-pmu",	1},
79	{"arm,cortex-a8-pmu",	1},
80	{"arm,cortex-a7-pmu",	1},
81	{"arm,cortex-a5-pmu",	1},
82	{"arm,arm11mpcore-pmu",	1},
83	{"arm,arm1176-pmu",	1},
84	{"arm,arm1136-pmu",	1},
85	{"qcom,krait-pmu",	1},
86	{NULL,			0}
87};
88
89static struct resource_spec pmu_spec[] = {
90	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
91	/* We don't currently handle pmu events, other than on cpu 0 */
92#ifdef notyet
93	{ SYS_RES_IRQ,		1,	RF_ACTIVE | RF_OPTIONAL },
94	{ SYS_RES_IRQ,		2,	RF_ACTIVE | RF_OPTIONAL },
95	{ SYS_RES_IRQ,		3,	RF_ACTIVE | RF_OPTIONAL },
96	{ SYS_RES_IRQ,		4,	RF_ACTIVE | RF_OPTIONAL },
97	{ SYS_RES_IRQ,		5,	RF_ACTIVE | RF_OPTIONAL },
98	{ SYS_RES_IRQ,		6,	RF_ACTIVE | RF_OPTIONAL },
99	{ SYS_RES_IRQ,		7,	RF_ACTIVE | RF_OPTIONAL },
100#endif
101	{ -1, 0 }
102};
103
104/* CCNT */
105#if __ARM_ARCH > 6
106int pmu_attched = 0;
107uint32_t ccnt_hi[MAXCPU];
108#endif
109
110#define	PMU_OVSR_C		0x80000000	/* Cycle Counter */
111#define	PMU_IESR_C		0x80000000	/* Cycle Counter */
112
113static int
114pmu_intr(void *arg)
115{
116#ifdef HWPMC_HOOKS
117	struct trapframe *tf;
118#endif
119	uint32_t r;
120#if defined(__arm__) && (__ARM_ARCH > 6)
121	u_int cpu;
122
123	cpu = PCPU_GET(cpuid);
124
125	r = cp15_pmovsr_get();
126	if (r & PMU_OVSR_C) {
127		atomic_add_32(&ccnt_hi[cpu], 1);
128		/* Clear the event. */
129		r &= ~PMU_OVSR_C;
130		cp15_pmovsr_set(PMU_OVSR_C);
131	}
132#else
133	r = 1;
134#endif
135
136#ifdef HWPMC_HOOKS
137	/* Only call into the HWPMC framework if we know there is work. */
138	if (r != 0 && pmc_intr) {
139		tf = arg;
140		(*pmc_intr)(PCPU_GET(cpuid), tf);
141	}
142#endif
143
144	return (FILTER_HANDLED);
145}
146
147static int
148pmu_probe(device_t dev)
149{
150
151	if (!ofw_bus_status_okay(dev))
152		return (ENXIO);
153
154	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
155		device_set_desc(dev, "Performance Monitoring Unit");
156		return (BUS_PROBE_DEFAULT);
157	}
158
159	return (ENXIO);
160}
161
162static int
163pmu_attach(device_t dev)
164{
165	struct pmu_softc *sc;
166#if defined(__arm__) && (__ARM_ARCH > 6)
167	uint32_t iesr;
168#endif
169	int err;
170	int i;
171
172	sc = device_get_softc(dev);
173	sc->dev = dev;
174
175	if (bus_alloc_resources(dev, pmu_spec, sc->res)) {
176		device_printf(dev, "could not allocate resources\n");
177		return (ENXIO);
178	}
179
180	/* Setup interrupt handler */
181	for (i = 0; i < MAX_RLEN; i++) {
182		if (sc->res[i] == NULL)
183			break;
184
185		err = bus_setup_intr(dev, sc->res[i], INTR_MPSAFE | INTR_TYPE_MISC,
186		    pmu_intr, NULL, NULL, &sc->ih[i]);
187		if (err) {
188			device_printf(dev, "Unable to setup interrupt handler.\n");
189			return (ENXIO);
190		}
191	}
192
193#if defined(__arm__) && (__ARM_ARCH > 6)
194	/* Initialize to 0. */
195	for (i = 0; i < MAXCPU; i++)
196		ccnt_hi[i] = 0;
197
198	/* Enable the interrupt to fire on overflow. */
199	iesr = cp15_pminten_get();
200	iesr |= PMU_IESR_C;
201	cp15_pminten_set(iesr);
202
203	/* Need this for getcyclecount() fast path. */
204	pmu_attched |= 1;
205#endif
206
207	return (0);
208}
209
210static device_method_t pmu_methods[] = {
211	DEVMETHOD(device_probe,		pmu_probe),
212	DEVMETHOD(device_attach,	pmu_attach),
213	{ 0, 0 }
214};
215
216static driver_t pmu_driver = {
217	"pmu",
218	pmu_methods,
219	sizeof(struct pmu_softc),
220};
221
222static devclass_t pmu_devclass;
223
224DRIVER_MODULE(pmu, simplebus, pmu_driver, pmu_devclass, 0, 0);
225