2 * Copyright (c) 2015 Imre Vadász <imre@vdsz.com>
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.
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.
17 /* sensor driver for effective CPU frequency, using APERF/MPERF MSRs */
19 #include <sys/param.h>
21 #include <sys/module.h>
22 #include <sys/sensors.h>
23 #include <sys/systm.h>
24 #include <sys/thread2.h>
26 #include <machine/clock.h>
27 #include <machine/cpufunc.h>
31 #define MSR_MPERF 0xE7
32 #define MSR_APERF 0xE8
35 struct ksensordev *sc_sensdev;
36 struct ksensor sc_sens;
37 struct sensor_task *sc_senstask;
39 uint64_t sc_aperf_prev;
40 uint64_t sc_mperf_prev;
42 uint32_t sc_flags; /* APERF_FLAG_ */
45 #define APERF_FLAG_PREV_VALID 0x1
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);
55 static void aperf_sensor_task(void *);
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),
67 static driver_t aperf_driver = {
70 sizeof(struct aperf_softc),
73 static devclass_t aperf_devclass;
74 DRIVER_MODULE(aperf, cpu, aperf_driver, aperf_devclass, NULL, NULL);
75 MODULE_VERSION(aperf, 1);
78 aperf_identify(driver_t *driver, device_t parent)
83 /* Make sure we're not being doubly invoked. */
84 if (device_find_child(parent, "aperf", -1) != NULL)
87 /* CPUID Fn0000_0006_ECX Effective Processor Frequency Interface */
88 do_cpuid(0x00000006, regs);
89 if ((regs[2] & 1) == 0)
92 child = device_add_child(parent, "aperf", -1);
94 device_printf(parent, "add aperf child failed\n");
98 aperf_probe(device_t dev)
100 if (resource_disabled("aperf", 0))
103 device_set_desc(dev, "CPU Frequency Sensor");
109 aperf_attach(device_t dev)
111 struct aperf_softc *sc = device_get_softc(dev);
115 parent = device_get_parent(dev);
116 cpu = device_get_unit(parent);
118 sc->sc_sensdev = CPU_GET_SENSDEV(parent);
119 if (sc->sc_sensdev == NULL)
123 * Add hw.sensors.cpuN.raw0 MIB.
125 ksnprintf(sc->sc_sens.desc, sizeof(sc->sc_sens.desc),
127 sc->sc_sens.type = SENSOR_FREQ;
128 sensor_set_unknown(&sc->sc_sens);
129 sensor_attach(sc->sc_sensdev, &sc->sc_sens);
131 sc->sc_senstask = sensor_task_register2(sc, aperf_sensor_task, 1, cpu);
137 aperf_detach(device_t dev)
139 struct aperf_softc *sc = device_get_softc(dev);
141 if (sc->sc_senstask != NULL)
142 sensor_task_unregister2(sc->sc_senstask);
144 if (sc->sc_sensdev != NULL)
145 sensor_detach(sc->sc_sensdev, &sc->sc_sens);
151 aperf_sensor_task(void *xsc)
153 struct aperf_softc *sc = xsc;
154 uint64_t aperf, mperf, tsc, freq;
155 uint64_t aperf_diff, mperf_diff, tsc_diff;
158 tsc = rdtsc_ordered();
159 aperf = rdmsr(MSR_APERF);
160 mperf = rdmsr(MSR_MPERF);
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;
171 aperf_diff = aperf - sc->sc_aperf_prev;
172 mperf_diff = mperf - sc->sc_mperf_prev;
174 tsc_diff = tsc - sc->sc_tsc_prev;
176 tsc_diff = tsc_frequency;
178 sc->sc_aperf_prev = aperf;
179 sc->sc_mperf_prev = mperf;
180 sc->sc_tsc_prev = tsc;
182 /* Avoid division by zero */
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);