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