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
perfbias_identify(driver_t * driver,device_t parent)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
perfbias_probe(device_t dev)112 perfbias_probe(device_t dev)
113 {
114 device_set_desc(dev, "CPU perf-energy bias");
115 return 0;
116 }
117
118 static int
perfbias_attach(device_t dev)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
perfbias_detach(device_t dev)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
perfbias_sysctl(SYSCTL_HANDLER_ARGS)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
perfbias_set_handler(struct cpuhelper_msg * msg)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
perfbias_set(struct perfbias_softc * sc,int hint)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