xref: /dragonfly/sys/dev/powermng/clockmod/clockmod.c (revision 31c7ac8b)
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/cpu_topology.h>
40 #include <sys/module.h>
41 #include <sys/queue.h>
42 #include <sys/serialize.h>
43 #include <sys/sysctl.h>
44 #include <sys/systm.h>
45 
46 #include <net/netmsg2.h>
47 #include <net/netisr2.h>
48 
49 #include <machine/specialreg.h>
50 #include <machine/cpufunc.h>
51 #include <machine/cputypes.h>
52 #include <machine/md_var.h>
53 
54 struct clockmod_dom;
55 
56 struct netmsg_clockmod {
57 	struct netmsg_base	base;
58 	uint64_t		ctl_value;
59 };
60 
61 struct clockmod_softc {
62 	TAILQ_ENTRY(clockmod_softc) sc_link;
63 	struct clockmod_dom	*sc_dom;
64 	int			sc_cpuid;
65 };
66 
67 struct clockmod_dom {
68 	TAILQ_ENTRY(clockmod_dom) dom_link;
69 	TAILQ_HEAD(, clockmod_softc) dom_list;
70 	struct sysctl_ctx_list	dom_sysctl_ctx;
71 	struct sysctl_oid	*dom_sysctl_tree;
72 	cpumask_t		dom_cpumask;
73 	char			dom_name[16];
74 	int			dom_select;
75 	uint32_t		dom_flags;
76 };
77 
78 #define CLOCKMOD_DOM_FLAG_ACTIVE	0x1
79 
80 struct clockmod_dom_ctrl {
81 	char			ctl_name[8];
82 	uint64_t		ctl_value;
83 };
84 
85 static int	clockmod_dom_attach(struct clockmod_softc *);
86 static void	clockmod_dom_detach(struct clockmod_softc *);
87 static struct clockmod_dom *clockmod_dom_find(cpumask_t);
88 static struct clockmod_dom *clockmod_dom_create(cpumask_t);
89 static void	clockmod_dom_destroy(struct clockmod_dom *);
90 
91 static int	clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS);
92 static int	clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS);
93 static int	clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS);
94 
95 static void	clockmod_identify(driver_t *, device_t);
96 static int	clockmod_probe(device_t);
97 static int	clockmod_attach(device_t);
98 static int	clockmod_detach(device_t);
99 
100 static void	clockmod_select_handler(netmsg_t);
101 static int	clockmod_select(const struct clockmod_softc *,
102 		    const struct clockmod_dom_ctrl *);
103 
104 static boolean_t clockmod_errata_duty(int);
105 
106 static struct lwkt_serialize clockmod_dom_slize = LWKT_SERIALIZE_INITIALIZER;
107 static int	clockmod_dom_id;
108 static TAILQ_HEAD(, clockmod_dom) clockmod_dom_list =
109     TAILQ_HEAD_INITIALIZER(clockmod_dom_list);
110 static int	clockmod_dom_nctrl;
111 static struct clockmod_dom_ctrl *clockmod_dom_controls;
112 
113 static device_method_t clockmod_methods[] = {
114 	/* Device interface */
115 	DEVMETHOD(device_identify,	clockmod_identify),
116 	DEVMETHOD(device_probe,		clockmod_probe),
117 	DEVMETHOD(device_attach,	clockmod_attach),
118 	DEVMETHOD(device_detach,	clockmod_detach),
119 
120 	DEVMETHOD_END
121 };
122 
123 static driver_t clockmod_driver = {
124 	"clockmod",
125 	clockmod_methods,
126 	sizeof(struct clockmod_softc),
127 };
128 
129 static devclass_t clockmod_devclass;
130 DRIVER_MODULE(clockmod, cpu, clockmod_driver, clockmod_devclass, NULL, NULL);
131 
132 static void
133 clockmod_identify(driver_t *driver, device_t parent)
134 {
135 	device_t child;
136 
137 	if (device_find_child(parent, "clockmod", -1) != NULL)
138 		return;
139 
140 	if (cpu_vendor_id != CPU_VENDOR_INTEL)
141 		return;
142 
143 	if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != (CPUID_ACPI | CPUID_TM))
144 		return;
145 
146 	child = device_add_child(parent, "clockmod", device_get_unit(parent));
147 	if (child == NULL)
148 		device_printf(parent, "add clockmod failed\n");
149 }
150 
151 static int
152 clockmod_probe(device_t dev)
153 {
154 	device_set_desc(dev, "CPU clock modulation");
155 	return 0;
156 }
157 
158 static int
159 clockmod_attach(device_t dev)
160 {
161 	struct clockmod_softc *sc = device_get_softc(dev);
162 	int error;
163 
164 	sc->sc_cpuid = device_get_unit(dev);
165 
166 	error = clockmod_dom_attach(sc);
167 	if (error) {
168 		device_printf(dev, "domain attach failed\n");
169 		return error;
170 	}
171 
172 	return 0;
173 }
174 
175 static int
176 clockmod_detach(device_t dev)
177 {
178 	clockmod_dom_detach(device_get_softc(dev));
179 	return 0;
180 }
181 
182 static int
183 clockmod_dom_attach(struct clockmod_softc *sc)
184 {
185 	struct clockmod_softc *sc1;
186 	struct clockmod_dom *dom;
187 	cpumask_t mask, found_mask = 0;
188 	int error = 0;
189 
190 	mask = get_cpumask_from_level(sc->sc_cpuid, CORE_LEVEL);
191 	if (mask == 0)
192 		mask = CPUMASK(sc->sc_cpuid);
193 
194 	lwkt_serialize_enter(&clockmod_dom_slize);
195 
196 	dom = clockmod_dom_find(mask);
197 	if (dom == NULL) {
198 		dom = clockmod_dom_create(mask);
199 		if (dom == NULL) {
200 			error = ENOMEM;
201 			goto back;
202 		}
203 	}
204 
205 	sc->sc_dom = dom;
206 	TAILQ_INSERT_TAIL(&dom->dom_list, sc, sc_link);
207 
208 	TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
209 		found_mask |= CPUMASK(sc1->sc_cpuid);
210 
211 	if (found_mask == dom->dom_cpumask) {
212 		/* All cpus in this domain is found */
213 		dom->dom_flags |= CLOCKMOD_DOM_FLAG_ACTIVE;
214 	}
215 back:
216 	lwkt_serialize_exit(&clockmod_dom_slize);
217 	return error;
218 }
219 
220 static void
221 clockmod_dom_detach(struct clockmod_softc *sc)
222 {
223 	struct clockmod_dom *dom;
224 
225 	lwkt_serialize_enter(&clockmod_dom_slize);
226 
227 	dom = sc->sc_dom;
228 	sc->sc_dom = NULL;
229 
230 	if (dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) {
231 		struct clockmod_softc *sc1;
232 
233 		/* Raise to 100% */
234 		TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
235 			clockmod_select(sc1, &clockmod_dom_controls[0]);
236 	}
237 
238 	/* One cpu is leaving; domain is no longer active */
239 	dom->dom_flags &= ~CLOCKMOD_DOM_FLAG_ACTIVE;
240 
241 	TAILQ_REMOVE(&dom->dom_list, sc, sc_link);
242 	if (TAILQ_EMPTY(&dom->dom_list))
243 		clockmod_dom_destroy(dom);
244 
245 	lwkt_serialize_exit(&clockmod_dom_slize);
246 }
247 
248 static struct clockmod_dom *
249 clockmod_dom_find(cpumask_t mask)
250 {
251 	struct clockmod_dom *dom;
252 
253 	TAILQ_FOREACH(dom, &clockmod_dom_list, dom_link) {
254 		if (dom->dom_cpumask == mask)
255 			return dom;
256 	}
257 	return NULL;
258 }
259 
260 static struct clockmod_dom *
261 clockmod_dom_create(cpumask_t mask)
262 {
263 	struct clockmod_dom *dom;
264 	int id;
265 
266 	id = clockmod_dom_id++;
267 	dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
268 
269 	TAILQ_INIT(&dom->dom_list);
270 	dom->dom_cpumask = mask;
271 	ksnprintf(dom->dom_name, sizeof(dom->dom_name), "clockmod_dom%d", id);
272 
273 	sysctl_ctx_init(&dom->dom_sysctl_ctx);
274 	dom->dom_sysctl_tree = SYSCTL_ADD_NODE(&dom->dom_sysctl_ctx,
275 	    SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, dom->dom_name,
276 	    CTLFLAG_RD, 0, "");
277 	if (dom->dom_sysctl_tree == NULL) {
278 		kprintf("%s: can't add sysctl node\n", dom->dom_name);
279 		kfree(dom, M_DEVBUF);
280 		return NULL;
281 	}
282 
283 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
284 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
285 	    OID_AUTO, "members", CTLTYPE_STRING | CTLFLAG_RD,
286 	    dom, 0, clockmod_dom_sysctl_members, "A", "member cpus");
287 
288 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
289 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
290 	    OID_AUTO, "available", CTLTYPE_STRING | CTLFLAG_RD,
291 	    dom, 0, clockmod_dom_sysctl_available, "A",
292 	    "available duty percent");
293 
294 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
295 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
296 	    OID_AUTO, "select", CTLTYPE_STRING | CTLFLAG_RW,
297 	    dom, 0, clockmod_dom_sysctl_select, "A", "select duty");
298 
299 	TAILQ_INSERT_TAIL(&clockmod_dom_list, dom, dom_link);
300 
301 	if (clockmod_dom_controls == NULL) {
302 		int nctrl, step, i, shift, cnt;
303 
304 #ifdef __x86_64__
305 		if (cpu_thermal_feature & CPUID_THERMAL_ECMD)
306 			shift = 0;
307 		else
308 #endif
309 			shift = 1;
310 
311 		nctrl = 8 << (1 - shift);
312 		step = 10000 / nctrl;
313 
314 		clockmod_dom_controls =
315 		    kmalloc(sizeof(struct clockmod_dom_ctrl) * nctrl, M_DEVBUF,
316 		    M_WAITOK | M_ZERO);
317 
318 		if (bootverbose)
319 			kprintf("clock modulation:\n");
320 
321 		cnt = 0;
322 		for (i = 0; i < nctrl; ++i) {
323 			struct clockmod_dom_ctrl *ctrl =
324 			    &clockmod_dom_controls[cnt];
325 			int duty;
326 
327 			duty = 10000 - (i * step);
328 			if (clockmod_errata_duty(duty))
329 				continue;
330 			++cnt;
331 
332 			ksnprintf(ctrl->ctl_name, sizeof(ctrl->ctl_name),
333 			    "%d.%02d%%", duty / 100, duty % 100);
334 			ctrl->ctl_value = (((nctrl - i) << shift) & 0xf);
335 			if (i != 0)
336 				ctrl->ctl_value |= 1 << 4;
337 
338 			if (bootverbose) {
339 				kprintf("  0x%04jx %s\n",
340 				    (uintmax_t)ctrl->ctl_value,
341 				    ctrl->ctl_name);
342 			}
343 		}
344 		clockmod_dom_nctrl = cnt;
345 	}
346 	return dom;
347 }
348 
349 static void
350 clockmod_dom_destroy(struct clockmod_dom *dom)
351 {
352 	KASSERT(TAILQ_EMPTY(&dom->dom_list),
353 	    ("%s: still has member cpus", dom->dom_name));
354 	TAILQ_REMOVE(&clockmod_dom_list, dom, dom_link);
355 
356 	sysctl_ctx_free(&dom->dom_sysctl_ctx);
357 	kfree(dom, M_DEVBUF);
358 
359 	if (TAILQ_EMPTY(&clockmod_dom_list)) {
360 		clockmod_dom_nctrl = 0;
361 		kfree(clockmod_dom_controls, M_DEVBUF);
362 		clockmod_dom_controls = NULL;
363 	}
364 }
365 
366 static int
367 clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)
368 {
369 	struct clockmod_dom *dom = arg1;
370 	struct clockmod_softc *sc;
371 	int loop, error;
372 
373 	lwkt_serialize_enter(&clockmod_dom_slize);
374 
375 	loop = error = 0;
376 	TAILQ_FOREACH(sc, &dom->dom_list, sc_link) {
377 		char buf[16];
378 
379 		if (error == 0 && loop)
380 			error = SYSCTL_OUT(req, " ", 1);
381 		if (error == 0) {
382 			ksnprintf(buf, sizeof(buf), "cpu%d", sc->sc_cpuid);
383 			error = SYSCTL_OUT(req, buf, strlen(buf));
384 		}
385 		++loop;
386 	}
387 
388 	lwkt_serialize_exit(&clockmod_dom_slize);
389 	return error;
390 }
391 
392 static int
393 clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)
394 {
395 	struct clockmod_dom *dom = arg1;
396 	int loop, error, i;
397 
398 	lwkt_serialize_enter(&clockmod_dom_slize);
399 
400 	if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
401 		error = SYSCTL_OUT(req, " ", 1);
402 		goto done;
403 	}
404 
405 	loop = error = 0;
406 	for (i = 0; i < clockmod_dom_nctrl; ++i) {
407 		if (error == 0 && loop)
408 			error = SYSCTL_OUT(req, " ", 1);
409 		if (error == 0) {
410 			error = SYSCTL_OUT(req,
411 			    clockmod_dom_controls[i].ctl_name,
412 			    strlen(clockmod_dom_controls[i].ctl_name));
413 		}
414 		++loop;
415 	}
416 done:
417 	lwkt_serialize_exit(&clockmod_dom_slize);
418 	return error;
419 }
420 
421 static int
422 clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)
423 {
424 	struct clockmod_dom *dom = arg1;
425 	struct clockmod_softc *sc;
426 	const struct clockmod_dom_ctrl *ctrl = NULL;
427 	char duty[16];
428 	int error, i;
429 
430 	lwkt_serialize_enter(&clockmod_dom_slize);
431 	KKASSERT(dom->dom_select >= 0 && dom->dom_select < clockmod_dom_nctrl);
432 	ksnprintf(duty, sizeof(duty), "%s",
433 	    clockmod_dom_controls[dom->dom_select].ctl_name);
434 	lwkt_serialize_exit(&clockmod_dom_slize);
435 
436 	error = sysctl_handle_string(oidp, duty, sizeof(duty), req);
437 	if (error != 0 || req->newptr == NULL)
438 		return error;
439 
440 	lwkt_serialize_enter(&clockmod_dom_slize);
441 
442 	if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
443 		error = EOPNOTSUPP;
444 		goto back;
445 	}
446 
447 	for (i = 0; i < clockmod_dom_nctrl; ++i) {
448 		ctrl = &clockmod_dom_controls[i];
449 		if (strcmp(duty, ctrl->ctl_name) == 0)
450 			break;
451 	}
452 	if (i == clockmod_dom_nctrl) {
453 		error = EINVAL;
454 		goto back;
455 	}
456 	dom->dom_select = i;
457 
458 	TAILQ_FOREACH(sc, &dom->dom_list, sc_link)
459 		clockmod_select(sc, ctrl);
460 back:
461 	lwkt_serialize_exit(&clockmod_dom_slize);
462 	return error;
463 }
464 
465 static void
466 clockmod_select_handler(netmsg_t msg)
467 {
468 	struct netmsg_clockmod *cmsg = (struct netmsg_clockmod *)msg;
469 
470 #if 0
471 	if (bootverbose) {
472 		kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid,
473 		    (uintmax_t)cmsg->ctl_value);
474 	}
475 #endif
476 
477 	wrmsr(MSR_THERM_CONTROL, cmsg->ctl_value);
478 	lwkt_replymsg(&cmsg->base.lmsg, 0);
479 }
480 
481 static int
482 clockmod_select(const struct clockmod_softc *sc,
483     const struct clockmod_dom_ctrl *ctrl)
484 {
485 	struct netmsg_clockmod msg;
486 
487 	netmsg_init(&msg.base, NULL, &curthread->td_msgport, MSGF_PRIORITY,
488 	    clockmod_select_handler);
489 	msg.ctl_value = ctrl->ctl_value;
490 	return lwkt_domsg(netisr_cpuport(sc->sc_cpuid), &msg.base.lmsg, 0);
491 }
492 
493 static boolean_t
494 clockmod_errata_duty(int duty)
495 {
496 	uint32_t model, stepping;
497 
498 	/*
499 	 * This is obtained from the original p4tcc code.
500 	 *
501 	 * The original errata checking code in p4tcc is obviously wrong.
502 	 * However, I am no longer being able to find the errata mentioned
503 	 * in the code.  The guess is that the errata only affects family
504 	 * 0x0f CPUs, since:
505 	 * - The errata applies to only to model 0x00, 0x01 and 0x02 in
506 	 *   the original p4tcc code.
507 	 * - Software controlled clock modulation has been supported since
508 	 *   0f_00 and the model of the oldest family 0x06 CPUs supporting
509 	 *   this feature is 0x09.
510 	 */
511 	if (CPUID_TO_FAMILY(cpu_id) != 0xf)
512 		return FALSE;
513 
514 	model = CPUID_TO_MODEL(cpu_id);
515 	stepping = cpu_id & 0xf;
516 
517 	if (model == 0x6) {
518 		switch (stepping) {
519 		case 0x2:
520 		case 0x4:
521 		case 0x5:
522 			/* Hang w/ 12.50% and 25.00% */
523 			if (duty == 1250 || duty == 2500)
524 				return TRUE;
525 			break;
526 		}
527 	} else if (model == 0x2) {
528 		switch (stepping) {
529 		case 0x2:
530 		case 0x4:
531 		case 0x5:
532 		case 0x7:
533 		case 0x9:
534 			/* Hang w/ 12.50% */
535 			if (duty == 1250)
536 				return TRUE;
537 			break;
538 		}
539 	} else if (model == 0x1) {
540 		switch (stepping) {
541 		case 0x2:
542 		case 0x3:
543 			/* Hang w/ 12.50% and 25.00% */
544 			if (duty == 1250 || duty == 2500)
545 				return TRUE;
546 			break;
547 		}
548 	} else if (model == 0x0) {
549 		switch (stepping) {
550 		case 0x7:
551 		case 0xa:
552 			/* Hang w/ 12.50% and 25.00% */
553 			if (duty == 1250 || duty == 2500)
554 				return TRUE;
555 			break;
556 		}
557 	}
558 	return FALSE;
559 }
560