Merge branch 'vendor/DIALOG'
[dragonfly.git] / sys / dev / misc / aperf / aperf.c
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 }