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_sysctl_handler(netmsg_t); 75 static int perfbias_sysctl(SYSCTL_HANDLER_ARGS); 76 77 static device_method_t perfbias_methods[] = { 78 /* Device interface */ 79 DEVMETHOD(device_identify, perfbias_identify), 80 DEVMETHOD(device_probe, perfbias_probe), 81 DEVMETHOD(device_attach, perfbias_attach), 82 DEVMETHOD(device_detach, perfbias_detach), 83 84 DEVMETHOD_END 85 }; 86 87 static driver_t perfbias_driver = { 88 "perfbias", 89 perfbias_methods, 90 sizeof(struct perfbias_softc), 91 }; 92 93 static devclass_t perfbias_devclass; 94 DRIVER_MODULE(perfbias, cpu, perfbias_driver, perfbias_devclass, NULL, NULL); 95 96 static void 97 perfbias_identify(driver_t *driver, device_t parent) 98 { 99 device_t child; 100 u_int regs[4]; 101 102 if (device_find_child(parent, "perfbias", -1) != NULL) 103 return; 104 105 if (cpu_high < 6 || cpu_vendor_id != CPU_VENDOR_INTEL) 106 return; 107 108 do_cpuid(6, regs); 109 if ((regs[2] & CPUID_THERMAL2_SETBH) == 0) 110 return; 111 112 child = device_add_child(parent, "perfbias", device_get_unit(parent)); 113 if (child == NULL) 114 device_printf(parent, "add perfbias failed\n"); 115 } 116 117 static int 118 perfbias_probe(device_t dev) 119 { 120 device_set_desc(dev, "CPU perf-energy bias"); 121 return 0; 122 } 123 124 static int 125 perfbias_attach(device_t dev) 126 { 127 struct perfbias_softc *sc = device_get_softc(dev); 128 129 sc->sc_dev = dev; 130 sc->sc_cpuid = device_get_unit(dev); 131 132 sysctl_ctx_init(&sc->sc_sysctl_ctx); 133 sc->sc_sysctl_tree = SYSCTL_ADD_NODE(&sc->sc_sysctl_ctx, 134 SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, 135 device_get_nameunit(sc->sc_dev), CTLFLAG_RD, 0, ""); 136 if (sc->sc_sysctl_tree == NULL) { 137 device_printf(sc->sc_dev, "can't add sysctl node\n"); 138 return ENOMEM; 139 } 140 141 SYSCTL_ADD_PROC(&sc->sc_sysctl_ctx, 142 SYSCTL_CHILDREN(sc->sc_sysctl_tree), 143 OID_AUTO, "hint", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 144 perfbias_sysctl, "I", "0 - highest perf; 15 - max energy saving"); 145 146 return 0; 147 } 148 149 static int 150 perfbias_detach(device_t dev) 151 { 152 struct perfbias_softc *sc = device_get_softc(dev); 153 154 if (sc->sc_sysctl_tree != NULL) 155 sysctl_ctx_free(&sc->sc_sysctl_ctx); 156 return 0; 157 } 158 159 static int 160 perfbias_sysctl(SYSCTL_HANDLER_ARGS) 161 { 162 struct perfbias_softc *sc = (void *)arg1; 163 struct netmsg_perfbias msg; 164 int error, hint; 165 166 hint = sc->sc_hint; 167 error = sysctl_handle_int(oidp, &hint, 0, req); 168 if (error || req->newptr == NULL) 169 return error; 170 if (hint < 0 || hint > INTEL_MSR_PERF_BIAS_HINTMASK) 171 return EINVAL; 172 173 netmsg_init(&msg.base, NULL, &curthread->td_msgport, 174 MSGF_PRIORITY, perfbias_sysctl_handler); 175 msg.hint = hint; 176 msg.sc = sc; 177 178 return lwkt_domsg(netisr_cpuport(sc->sc_cpuid), &msg.base.lmsg, 0); 179 } 180 181 static void 182 perfbias_sysctl_handler(netmsg_t msg) 183 { 184 struct netmsg_perfbias *pmsg = (struct netmsg_perfbias *)msg; 185 struct perfbias_softc *sc = pmsg->sc; 186 uint64_t hint = pmsg->hint; 187 188 wrmsr(INTEL_MSR_PERF_BIAS, hint); 189 hint = rdmsr(INTEL_MSR_PERF_BIAS); 190 191 sc->sc_hint = hint & INTEL_MSR_PERF_BIAS_HINTMASK; 192 193 lwkt_replymsg(&pmsg->base.lmsg, 0); 194 } 195