xref: /dragonfly/sys/dev/misc/aperf/aperf.c (revision 279dd846)
1 /*
2  * Copyright (c) 2015 Imre Vadász <imre@vdsz.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /* sensor driver for effective CPU frequency, using APERF/MPERF MSRs */
18 
19 #include <sys/param.h>
20 #include <sys/bus.h>
21 #include <sys/module.h>
22 #include <sys/sensors.h>
23 #include <sys/systm.h>
24 #include <sys/thread2.h>
25 
26 #include <machine/clock.h>
27 #include <machine/cpufunc.h>
28 
29 #include "cpu_if.h"
30 
31 #define MSR_MPERF	0xE7
32 #define MSR_APERF	0xE8
33 
34 struct aperf_softc {
35 	struct ksensordev	*sc_sensdev;
36 	struct ksensor		sc_sens;
37 	struct sensor_task	*sc_senstask;
38 
39 	uint64_t		sc_aperf_prev;
40 	uint64_t		sc_mperf_prev;
41 	uint64_t		sc_tsc_prev;
42 	uint32_t		sc_flags;	/* APERF_FLAG_ */
43 };
44 
45 #define APERF_FLAG_PREV_VALID	0x1
46 
47 /*
48  * Device methods.
49  */
50 static void	aperf_identify(driver_t *, device_t);
51 static int	aperf_probe(device_t);
52 static int	aperf_attach(device_t);
53 static int	aperf_detach(device_t);
54 
55 static void	aperf_sensor_task(void *);
56 
57 static device_method_t aperf_methods[] = {
58 	/* Device interface */
59 	DEVMETHOD(device_identify,	aperf_identify),
60 	DEVMETHOD(device_probe,		aperf_probe),
61 	DEVMETHOD(device_attach,	aperf_attach),
62 	DEVMETHOD(device_detach,	aperf_detach),
63 
64 	DEVMETHOD_END
65 };
66 
67 static driver_t aperf_driver = {
68 	"aperf",
69 	aperf_methods,
70 	sizeof(struct aperf_softc),
71 };
72 
73 static devclass_t aperf_devclass;
74 DRIVER_MODULE(aperf, cpu, aperf_driver, aperf_devclass, NULL, NULL);
75 MODULE_VERSION(aperf, 1);
76 
77 static void
78 aperf_identify(driver_t *driver, device_t parent)
79 {
80 	uint32_t regs[4];
81 	device_t child;
82 
83 	/* Make sure we're not being doubly invoked. */
84 	if (device_find_child(parent, "aperf", -1) != NULL)
85 		return;
86 
87 	/* CPUID Fn0000_0006_ECX Effective Processor Frequency Interface */
88 	do_cpuid(0x00000006, regs);
89 	if ((regs[2] & 1) == 0)
90 		return;
91 
92 	child = device_add_child(parent, "aperf", -1);
93 	if (child == NULL)
94 		device_printf(parent, "add aperf child failed\n");
95 }
96 
97 static int
98 aperf_probe(device_t dev)
99 {
100 	if (resource_disabled("aperf", 0))
101 		return ENXIO;
102 
103 	device_set_desc(dev, "CPU Frequency Sensor");
104 
105 	return 0;
106 }
107 
108 static int
109 aperf_attach(device_t dev)
110 {
111 	struct aperf_softc *sc = device_get_softc(dev);
112 	device_t parent;
113 	int cpu;
114 
115 	parent = device_get_parent(dev);
116 	cpu = device_get_unit(parent);
117 
118 	sc->sc_sensdev = CPU_GET_SENSDEV(parent);
119 	if (sc->sc_sensdev == NULL)
120 		return ENXIO;
121 
122 	/*
123 	 * Add hw.sensors.cpuN.raw0 MIB.
124 	 */
125 	ksnprintf(sc->sc_sens.desc, sizeof(sc->sc_sens.desc),
126 	    "cpu%d freq", cpu);
127 	sc->sc_sens.type = SENSOR_FREQ;
128 	sensor_set_unknown(&sc->sc_sens);
129 	sensor_attach(sc->sc_sensdev, &sc->sc_sens);
130 
131 	sc->sc_senstask = sensor_task_register2(sc, aperf_sensor_task, 1, cpu);
132 
133 	return 0;
134 }
135 
136 static int
137 aperf_detach(device_t dev)
138 {
139 	struct aperf_softc *sc = device_get_softc(dev);
140 
141 	if (sc->sc_senstask != NULL)
142 		sensor_task_unregister2(sc->sc_senstask);
143 
144 	if (sc->sc_sensdev != NULL)
145 		sensor_detach(sc->sc_sensdev, &sc->sc_sens);
146 
147 	return 0;
148 }
149 
150 static void
151 aperf_sensor_task(void *xsc)
152 {
153 	struct aperf_softc *sc = xsc;
154 	uint64_t aperf, mperf, tsc, freq;
155 	uint64_t aperf_diff, mperf_diff, tsc_diff;
156 
157 	crit_enter();
158 	tsc = rdtsc_ordered();
159 	aperf = rdmsr(MSR_APERF);
160 	mperf = rdmsr(MSR_MPERF);
161 	crit_exit();
162 
163 	if ((sc->sc_flags & APERF_FLAG_PREV_VALID) == 0) {
164 		sc->sc_aperf_prev = aperf;
165 		sc->sc_mperf_prev = mperf;
166 		sc->sc_tsc_prev = tsc;
167 		sc->sc_flags |= APERF_FLAG_PREV_VALID;
168 		return;
169 	}
170 
171 	aperf_diff = aperf - sc->sc_aperf_prev;
172 	mperf_diff = mperf - sc->sc_mperf_prev;
173 	if (tsc_invariant)
174 		tsc_diff = tsc - sc->sc_tsc_prev;
175 	else
176 		tsc_diff = tsc_frequency;
177 
178 	sc->sc_aperf_prev = aperf;
179 	sc->sc_mperf_prev = mperf;
180 	sc->sc_tsc_prev = tsc;
181 
182 	/* Avoid division by zero */
183 	if (mperf_diff == 0)
184 		mperf_diff = 1;
185 
186 	/* Using tsc_diff/1000 to avoid overflowing */
187 	freq = ((aperf_diff * (tsc_diff / 1000)) / mperf_diff) * 1000;
188 	sensor_set(&sc->sc_sens, freq, SENSOR_S_UNSPEC);
189 }
190