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