xref: /dragonfly/sys/dev/powermng/clockmod/clockmod.c (revision f7df6c8e)
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;
188 	int error = 0;
189 
190 	CPUMASK_ASSZERO(found_mask);
191 
192 	mask = get_cpumask_from_level(sc->sc_cpuid, CORE_LEVEL);
193 	if (CPUMASK_TESTZERO(mask))
194 		CPUMASK_ASSBIT(mask, sc->sc_cpuid);
195 
196 	lwkt_serialize_enter(&clockmod_dom_slize);
197 
198 	dom = clockmod_dom_find(mask);
199 	if (dom == NULL) {
200 		dom = clockmod_dom_create(mask);
201 		if (dom == NULL) {
202 			error = ENOMEM;
203 			goto back;
204 		}
205 	}
206 
207 	sc->sc_dom = dom;
208 	TAILQ_INSERT_TAIL(&dom->dom_list, sc, sc_link);
209 
210 	TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
211 		CPUMASK_ORBIT(found_mask, sc1->sc_cpuid);
212 
213 	if (CPUMASK_CMPMASKEQ(found_mask, dom->dom_cpumask)) {
214 		/* All cpus in this domain is found */
215 		dom->dom_flags |= CLOCKMOD_DOM_FLAG_ACTIVE;
216 	}
217 back:
218 	lwkt_serialize_exit(&clockmod_dom_slize);
219 	return error;
220 }
221 
222 static void
223 clockmod_dom_detach(struct clockmod_softc *sc)
224 {
225 	struct clockmod_dom *dom;
226 
227 	lwkt_serialize_enter(&clockmod_dom_slize);
228 
229 	dom = sc->sc_dom;
230 	sc->sc_dom = NULL;
231 
232 	if (dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) {
233 		struct clockmod_softc *sc1;
234 
235 		/* Raise to 100% */
236 		TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
237 			clockmod_select(sc1, &clockmod_dom_controls[0]);
238 	}
239 
240 	/* One cpu is leaving; domain is no longer active */
241 	dom->dom_flags &= ~CLOCKMOD_DOM_FLAG_ACTIVE;
242 
243 	TAILQ_REMOVE(&dom->dom_list, sc, sc_link);
244 	if (TAILQ_EMPTY(&dom->dom_list))
245 		clockmod_dom_destroy(dom);
246 
247 	lwkt_serialize_exit(&clockmod_dom_slize);
248 }
249 
250 static struct clockmod_dom *
251 clockmod_dom_find(cpumask_t mask)
252 {
253 	struct clockmod_dom *dom;
254 
255 	TAILQ_FOREACH(dom, &clockmod_dom_list, dom_link) {
256 		if (CPUMASK_CMPMASKEQ(dom->dom_cpumask, mask))
257 			return dom;
258 	}
259 	return NULL;
260 }
261 
262 static struct clockmod_dom *
263 clockmod_dom_create(cpumask_t mask)
264 {
265 	struct clockmod_dom *dom;
266 	int id;
267 
268 	id = clockmod_dom_id++;
269 	dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
270 
271 	TAILQ_INIT(&dom->dom_list);
272 	dom->dom_cpumask = mask;
273 	ksnprintf(dom->dom_name, sizeof(dom->dom_name), "clockmod_dom%d", id);
274 
275 	sysctl_ctx_init(&dom->dom_sysctl_ctx);
276 	dom->dom_sysctl_tree = SYSCTL_ADD_NODE(&dom->dom_sysctl_ctx,
277 	    SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, dom->dom_name,
278 	    CTLFLAG_RD, 0, "");
279 	if (dom->dom_sysctl_tree == NULL) {
280 		kprintf("%s: can't add sysctl node\n", dom->dom_name);
281 		kfree(dom, M_DEVBUF);
282 		return NULL;
283 	}
284 
285 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
286 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
287 	    OID_AUTO, "members", CTLTYPE_STRING | CTLFLAG_RD,
288 	    dom, 0, clockmod_dom_sysctl_members, "A", "member cpus");
289 
290 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
291 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
292 	    OID_AUTO, "available", CTLTYPE_STRING | CTLFLAG_RD,
293 	    dom, 0, clockmod_dom_sysctl_available, "A",
294 	    "available duty percent");
295 
296 	SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
297 	    SYSCTL_CHILDREN(dom->dom_sysctl_tree),
298 	    OID_AUTO, "select", CTLTYPE_STRING | CTLFLAG_RW,
299 	    dom, 0, clockmod_dom_sysctl_select, "A", "select duty");
300 
301 	TAILQ_INSERT_TAIL(&clockmod_dom_list, dom, dom_link);
302 
303 	if (clockmod_dom_controls == NULL) {
304 		int nctrl, step, i, shift, cnt;
305 
306 #ifdef __x86_64__
307 		if (cpu_thermal_feature & CPUID_THERMAL_ECMD)
308 			shift = 0;
309 		else
310 #endif
311 			shift = 1;
312 
313 		nctrl = 8 << (1 - shift);
314 		step = 10000 / nctrl;
315 
316 		clockmod_dom_controls =
317 		    kmalloc(sizeof(struct clockmod_dom_ctrl) * nctrl, M_DEVBUF,
318 		    M_WAITOK | M_ZERO);
319 
320 		if (bootverbose)
321 			kprintf("clock modulation:\n");
322 
323 		cnt = 0;
324 		for (i = 0; i < nctrl; ++i) {
325 			struct clockmod_dom_ctrl *ctrl =
326 			    &clockmod_dom_controls[cnt];
327 			int duty;
328 
329 			duty = 10000 - (i * step);
330 			if (clockmod_errata_duty(duty))
331 				continue;
332 			++cnt;
333 
334 			ksnprintf(ctrl->ctl_name, sizeof(ctrl->ctl_name),
335 			    "%d.%02d%%", duty / 100, duty % 100);
336 			ctrl->ctl_value = (((nctrl - i) << shift) & 0xf);
337 			if (i != 0)
338 				ctrl->ctl_value |= 1 << 4;
339 
340 			if (bootverbose) {
341 				kprintf("  0x%04jx %s\n",
342 				    (uintmax_t)ctrl->ctl_value,
343 				    ctrl->ctl_name);
344 			}
345 		}
346 		clockmod_dom_nctrl = cnt;
347 	}
348 	return dom;
349 }
350 
351 static void
352 clockmod_dom_destroy(struct clockmod_dom *dom)
353 {
354 	KASSERT(TAILQ_EMPTY(&dom->dom_list),
355 	    ("%s: still has member cpus", dom->dom_name));
356 	TAILQ_REMOVE(&clockmod_dom_list, dom, dom_link);
357 
358 	sysctl_ctx_free(&dom->dom_sysctl_ctx);
359 	kfree(dom, M_DEVBUF);
360 
361 	if (TAILQ_EMPTY(&clockmod_dom_list)) {
362 		clockmod_dom_nctrl = 0;
363 		kfree(clockmod_dom_controls, M_DEVBUF);
364 		clockmod_dom_controls = NULL;
365 	}
366 }
367 
368 static int
369 clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)
370 {
371 	struct clockmod_dom *dom = arg1;
372 	struct clockmod_softc *sc;
373 	int loop, error;
374 
375 	lwkt_serialize_enter(&clockmod_dom_slize);
376 
377 	loop = error = 0;
378 	TAILQ_FOREACH(sc, &dom->dom_list, sc_link) {
379 		char buf[16];
380 
381 		if (error == 0 && loop)
382 			error = SYSCTL_OUT(req, " ", 1);
383 		if (error == 0) {
384 			ksnprintf(buf, sizeof(buf), "cpu%d", sc->sc_cpuid);
385 			error = SYSCTL_OUT(req, buf, strlen(buf));
386 		}
387 		++loop;
388 	}
389 
390 	lwkt_serialize_exit(&clockmod_dom_slize);
391 	return error;
392 }
393 
394 static int
395 clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)
396 {
397 	struct clockmod_dom *dom = arg1;
398 	int loop, error, i;
399 
400 	lwkt_serialize_enter(&clockmod_dom_slize);
401 
402 	if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
403 		error = SYSCTL_OUT(req, " ", 1);
404 		goto done;
405 	}
406 
407 	loop = error = 0;
408 	for (i = 0; i < clockmod_dom_nctrl; ++i) {
409 		if (error == 0 && loop)
410 			error = SYSCTL_OUT(req, " ", 1);
411 		if (error == 0) {
412 			error = SYSCTL_OUT(req,
413 			    clockmod_dom_controls[i].ctl_name,
414 			    strlen(clockmod_dom_controls[i].ctl_name));
415 		}
416 		++loop;
417 	}
418 done:
419 	lwkt_serialize_exit(&clockmod_dom_slize);
420 	return error;
421 }
422 
423 static int
424 clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)
425 {
426 	struct clockmod_dom *dom = arg1;
427 	struct clockmod_softc *sc;
428 	const struct clockmod_dom_ctrl *ctrl = NULL;
429 	char duty[16];
430 	int error, i;
431 
432 	lwkt_serialize_enter(&clockmod_dom_slize);
433 	KKASSERT(dom->dom_select >= 0 && dom->dom_select < clockmod_dom_nctrl);
434 	ksnprintf(duty, sizeof(duty), "%s",
435 	    clockmod_dom_controls[dom->dom_select].ctl_name);
436 	lwkt_serialize_exit(&clockmod_dom_slize);
437 
438 	error = sysctl_handle_string(oidp, duty, sizeof(duty), req);
439 	if (error != 0 || req->newptr == NULL)
440 		return error;
441 
442 	lwkt_serialize_enter(&clockmod_dom_slize);
443 
444 	if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
445 		error = EOPNOTSUPP;
446 		goto back;
447 	}
448 
449 	for (i = 0; i < clockmod_dom_nctrl; ++i) {
450 		ctrl = &clockmod_dom_controls[i];
451 		if (strcmp(duty, ctrl->ctl_name) == 0)
452 			break;
453 	}
454 	if (i == clockmod_dom_nctrl) {
455 		error = EINVAL;
456 		goto back;
457 	}
458 	dom->dom_select = i;
459 
460 	TAILQ_FOREACH(sc, &dom->dom_list, sc_link)
461 		clockmod_select(sc, ctrl);
462 back:
463 	lwkt_serialize_exit(&clockmod_dom_slize);
464 	return error;
465 }
466 
467 static void
468 clockmod_select_handler(netmsg_t msg)
469 {
470 	struct netmsg_clockmod *cmsg = (struct netmsg_clockmod *)msg;
471 
472 #if 0
473 	if (bootverbose) {
474 		kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid,
475 		    (uintmax_t)cmsg->ctl_value);
476 	}
477 #endif
478 
479 	wrmsr(MSR_THERM_CONTROL, cmsg->ctl_value);
480 	lwkt_replymsg(&cmsg->base.lmsg, 0);
481 }
482 
483 static int
484 clockmod_select(const struct clockmod_softc *sc,
485     const struct clockmod_dom_ctrl *ctrl)
486 {
487 	struct netmsg_clockmod msg;
488 
489 	netmsg_init(&msg.base, NULL, &curthread->td_msgport, MSGF_PRIORITY,
490 	    clockmod_select_handler);
491 	msg.ctl_value = ctrl->ctl_value;
492 	return lwkt_domsg(netisr_cpuport(sc->sc_cpuid), &msg.base.lmsg, 0);
493 }
494 
495 static boolean_t
496 clockmod_errata_duty(int duty)
497 {
498 	uint32_t model, stepping;
499 
500 	/*
501 	 * This is obtained from the original p4tcc code.
502 	 *
503 	 * The original errata checking code in p4tcc is obviously wrong.
504 	 * However, I am no longer being able to find the errata mentioned
505 	 * in the code.  The guess is that the errata only affects family
506 	 * 0x0f CPUs, since:
507 	 * - The errata applies to only to model 0x00, 0x01 and 0x02 in
508 	 *   the original p4tcc code.
509 	 * - Software controlled clock modulation has been supported since
510 	 *   0f_00 and the model of the oldest family 0x06 CPUs supporting
511 	 *   this feature is 0x09.
512 	 */
513 	if (CPUID_TO_FAMILY(cpu_id) != 0xf)
514 		return FALSE;
515 
516 	model = CPUID_TO_MODEL(cpu_id);
517 	stepping = cpu_id & 0xf;
518 
519 	if (model == 0x6) {
520 		switch (stepping) {
521 		case 0x2:
522 		case 0x4:
523 		case 0x5:
524 			/* Hang w/ 12.50% and 25.00% */
525 			if (duty == 1250 || duty == 2500)
526 				return TRUE;
527 			break;
528 		}
529 	} else if (model == 0x2) {
530 		switch (stepping) {
531 		case 0x2:
532 		case 0x4:
533 		case 0x5:
534 		case 0x7:
535 		case 0x9:
536 			/* Hang w/ 12.50% */
537 			if (duty == 1250)
538 				return TRUE;
539 			break;
540 		}
541 	} else if (model == 0x1) {
542 		switch (stepping) {
543 		case 0x2:
544 		case 0x3:
545 			/* Hang w/ 12.50% and 25.00% */
546 			if (duty == 1250 || duty == 2500)
547 				return TRUE;
548 			break;
549 		}
550 	} else if (model == 0x0) {
551 		switch (stepping) {
552 		case 0x7:
553 		case 0xa:
554 			/* Hang w/ 12.50% and 25.00% */
555 			if (duty == 1250 || duty == 2500)
556 				return TRUE;
557 			break;
558 		}
559 	}
560 	return FALSE;
561 }
562