xref: /dragonfly/sys/dev/powermng/perfbias/perfbias.c (revision 0db87cb7)
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