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