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/module.h> 40 #include <sys/sysctl.h> 41 #include <sys/systm.h> 42 43 #include <net/netmsg2.h> 44 #include <net/netisr2.h> 45 46 #include <machine/specialreg.h> 47 #include <machine/cpufunc.h> 48 #include <machine/cputypes.h> 49 #include <machine/md_var.h> 50 51 #define INTEL_MSR_PERF_BIAS 0x1b0 52 #define INTEL_MSR_PERF_BIAS_HINTMASK 0xf 53 54 struct perfbias_softc { 55 device_t sc_dev; 56 int sc_cpuid; 57 int sc_hint; 58 59 struct sysctl_ctx_list sc_sysctl_ctx; 60 struct sysctl_oid *sc_sysctl_tree; 61 }; 62 63 struct netmsg_perfbias { 64 struct netmsg_base base; 65 struct perfbias_softc *sc; 66 int hint; 67 }; 68 69 static void perfbias_identify(driver_t *, device_t); 70 static int perfbias_probe(device_t); 71 static int perfbias_attach(device_t); 72 static int perfbias_detach(device_t); 73 74 static void perfbias_set_handler(netmsg_t); 75 static int perfbias_set(struct perfbias_softc *, int); 76 77 static int perfbias_sysctl(SYSCTL_HANDLER_ARGS); 78 79 static device_method_t perfbias_methods[] = { 80 /* Device interface */ 81 DEVMETHOD(device_identify, perfbias_identify), 82 DEVMETHOD(device_probe, perfbias_probe), 83 DEVMETHOD(device_attach, perfbias_attach), 84 DEVMETHOD(device_detach, perfbias_detach), 85 86 DEVMETHOD_END 87 }; 88 89 static driver_t perfbias_driver = { 90 "perfbias", 91 perfbias_methods, 92 sizeof(struct perfbias_softc), 93 }; 94 95 static devclass_t perfbias_devclass; 96 DRIVER_MODULE(perfbias, cpu, perfbias_driver, perfbias_devclass, NULL, NULL); 97 98 static void 99 perfbias_identify(driver_t *driver, device_t parent) 100 { 101 device_t child; 102 u_int regs[4]; 103 104 if (device_find_child(parent, "perfbias", -1) != NULL) 105 return; 106 107 if (cpu_high < 6 || cpu_vendor_id != CPU_VENDOR_INTEL) 108 return; 109 110 do_cpuid(6, regs); 111 if ((regs[2] & CPUID_THERMAL2_SETBH) == 0) 112 return; 113 114 child = device_add_child(parent, "perfbias", device_get_unit(parent)); 115 if (child == NULL) 116 device_printf(parent, "add perfbias failed\n"); 117 } 118 119 static int 120 perfbias_probe(device_t dev) 121 { 122 device_set_desc(dev, "CPU perf-energy bias"); 123 return 0; 124 } 125 126 static int 127 perfbias_attach(device_t dev) 128 { 129 struct perfbias_softc *sc = device_get_softc(dev); 130 131 sc->sc_dev = dev; 132 sc->sc_cpuid = device_get_unit(dev); 133 134 sysctl_ctx_init(&sc->sc_sysctl_ctx); 135 sc->sc_sysctl_tree = SYSCTL_ADD_NODE(&sc->sc_sysctl_ctx, 136 SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, 137 device_get_nameunit(sc->sc_dev), CTLFLAG_RD, 0, ""); 138 if (sc->sc_sysctl_tree == NULL) { 139 device_printf(sc->sc_dev, "can't add sysctl node\n"); 140 return ENOMEM; 141 } 142 143 SYSCTL_ADD_PROC(&sc->sc_sysctl_ctx, 144 SYSCTL_CHILDREN(sc->sc_sysctl_tree), 145 OID_AUTO, "hint", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 146 perfbias_sysctl, "I", "0 - highest perf; 15 - max energy saving"); 147 148 return 0; 149 } 150 151 static int 152 perfbias_detach(device_t dev) 153 { 154 struct perfbias_softc *sc = device_get_softc(dev); 155 156 if (sc->sc_sysctl_tree != NULL) 157 sysctl_ctx_free(&sc->sc_sysctl_ctx); 158 159 /* Restore to highest performance */ 160 perfbias_set(sc, 0); 161 return 0; 162 } 163 164 static int 165 perfbias_sysctl(SYSCTL_HANDLER_ARGS) 166 { 167 struct perfbias_softc *sc = (void *)arg1; 168 int error, hint; 169 170 hint = sc->sc_hint; 171 error = sysctl_handle_int(oidp, &hint, 0, req); 172 if (error || req->newptr == NULL) 173 return error; 174 if (hint < 0 || hint > INTEL_MSR_PERF_BIAS_HINTMASK) 175 return EINVAL; 176 177 return perfbias_set(sc, hint); 178 } 179 180 static void 181 perfbias_set_handler(netmsg_t msg) 182 { 183 struct netmsg_perfbias *pmsg = (struct netmsg_perfbias *)msg; 184 struct perfbias_softc *sc = pmsg->sc; 185 uint64_t hint = pmsg->hint; 186 187 wrmsr(INTEL_MSR_PERF_BIAS, hint); 188 hint = rdmsr(INTEL_MSR_PERF_BIAS); 189 190 sc->sc_hint = hint & INTEL_MSR_PERF_BIAS_HINTMASK; 191 192 lwkt_replymsg(&pmsg->base.lmsg, 0); 193 } 194 195 static int 196 perfbias_set(struct perfbias_softc *sc, int hint) 197 { 198 struct netmsg_perfbias msg; 199 200 netmsg_init(&msg.base, NULL, &curthread->td_msgport, MSGF_PRIORITY, 201 perfbias_set_handler); 202 msg.hint = hint; 203 msg.sc = sc; 204 205 return lwkt_domsg(netisr_cpuport(sc->sc_cpuid), &msg.base.lmsg, 0); 206 } 207