1 /* 2 * Copyright (c) 2014 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Sepherosa Ziehau <sepherosa@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/conf.h> 37 #include <sys/kernel.h> 38 #include <sys/bus.h> 39 #include <sys/cpuhelper.h> 40 #include <sys/module.h> 41 #include <sys/sysctl.h> 42 #include <sys/systm.h> 43 44 #include <machine/specialreg.h> 45 #include <machine/cpufunc.h> 46 #include <machine/cputypes.h> 47 #include <machine/md_var.h> 48 49 #define INTEL_MSR_PERF_BIAS 0x1b0 50 #define INTEL_MSR_PERF_BIAS_HINTMASK 0xf 51 52 struct perfbias_softc { 53 device_t sc_dev; 54 int sc_cpuid; 55 int sc_hint; 56 57 struct sysctl_ctx_list sc_sysctl_ctx; 58 struct sysctl_oid *sc_sysctl_tree; 59 }; 60 61 static void perfbias_identify(driver_t *, device_t); 62 static int perfbias_probe(device_t); 63 static int perfbias_attach(device_t); 64 static int perfbias_detach(device_t); 65 66 static void perfbias_set_handler(struct cpuhelper_msg *); 67 static int perfbias_set(struct perfbias_softc *, int); 68 69 static int perfbias_sysctl(SYSCTL_HANDLER_ARGS); 70 71 static device_method_t perfbias_methods[] = { 72 /* Device interface */ 73 DEVMETHOD(device_identify, perfbias_identify), 74 DEVMETHOD(device_probe, perfbias_probe), 75 DEVMETHOD(device_attach, perfbias_attach), 76 DEVMETHOD(device_detach, perfbias_detach), 77 78 DEVMETHOD_END 79 }; 80 81 static driver_t perfbias_driver = { 82 "perfbias", 83 perfbias_methods, 84 sizeof(struct perfbias_softc), 85 }; 86 87 static devclass_t perfbias_devclass; 88 DRIVER_MODULE(perfbias, cpu, perfbias_driver, perfbias_devclass, NULL, NULL); 89 90 static void 91 perfbias_identify(driver_t *driver, device_t parent) 92 { 93 device_t child; 94 u_int regs[4]; 95 96 if (device_find_child(parent, "perfbias", -1) != NULL) 97 return; 98 99 if (cpu_high < 6 || cpu_vendor_id != CPU_VENDOR_INTEL) 100 return; 101 102 do_cpuid(6, regs); 103 if ((regs[2] & CPUID_THERMAL2_SETBH) == 0) 104 return; 105 106 child = device_add_child(parent, "perfbias", device_get_unit(parent)); 107 if (child == NULL) 108 device_printf(parent, "add perfbias failed\n"); 109 } 110 111 static int 112 perfbias_probe(device_t dev) 113 { 114 device_set_desc(dev, "CPU perf-energy bias"); 115 return 0; 116 } 117 118 static int 119 perfbias_attach(device_t dev) 120 { 121 struct perfbias_softc *sc = device_get_softc(dev); 122 123 sc->sc_dev = dev; 124 sc->sc_cpuid = device_get_unit(dev); 125 126 sysctl_ctx_init(&sc->sc_sysctl_ctx); 127 sc->sc_sysctl_tree = SYSCTL_ADD_NODE(&sc->sc_sysctl_ctx, 128 SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, 129 device_get_nameunit(sc->sc_dev), CTLFLAG_RD, 0, ""); 130 if (sc->sc_sysctl_tree == NULL) { 131 device_printf(sc->sc_dev, "can't add sysctl node\n"); 132 return ENOMEM; 133 } 134 135 SYSCTL_ADD_PROC(&sc->sc_sysctl_ctx, 136 SYSCTL_CHILDREN(sc->sc_sysctl_tree), 137 OID_AUTO, "hint", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 138 perfbias_sysctl, "I", "0 - highest perf; 15 - max energy saving"); 139 140 return 0; 141 } 142 143 static int 144 perfbias_detach(device_t dev) 145 { 146 struct perfbias_softc *sc = device_get_softc(dev); 147 148 if (sc->sc_sysctl_tree != NULL) 149 sysctl_ctx_free(&sc->sc_sysctl_ctx); 150 151 /* Restore to highest performance */ 152 perfbias_set(sc, 0); 153 return 0; 154 } 155 156 static int 157 perfbias_sysctl(SYSCTL_HANDLER_ARGS) 158 { 159 struct perfbias_softc *sc = (void *)arg1; 160 int error, hint; 161 162 hint = sc->sc_hint; 163 error = sysctl_handle_int(oidp, &hint, 0, req); 164 if (error || req->newptr == NULL) 165 return error; 166 if (hint < 0 || hint > INTEL_MSR_PERF_BIAS_HINTMASK) 167 return EINVAL; 168 169 return perfbias_set(sc, hint); 170 } 171 172 static void 173 perfbias_set_handler(struct cpuhelper_msg *msg) 174 { 175 struct perfbias_softc *sc = msg->ch_cbarg; 176 uint64_t hint = msg->ch_cbarg1; 177 178 wrmsr(INTEL_MSR_PERF_BIAS, hint); 179 hint = rdmsr(INTEL_MSR_PERF_BIAS); 180 181 sc->sc_hint = hint & INTEL_MSR_PERF_BIAS_HINTMASK; 182 183 cpuhelper_replymsg(msg, 0); 184 } 185 186 static int 187 perfbias_set(struct perfbias_softc *sc, int hint) 188 { 189 struct cpuhelper_msg msg; 190 191 cpuhelper_initmsg(&msg, &curthread->td_msgport, 192 perfbias_set_handler, sc, MSGF_PRIORITY); 193 msg.ch_cbarg1 = hint; 194 195 return (cpuhelper_domsg(&msg, sc->sc_cpuid)); 196 } 197