2 * Copyright (c) 2014 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Sepherosa Ziehau <sepherosa@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
35 #include <sys/param.h>
37 #include <sys/kernel.h>
39 #include <sys/cpu_topology.h>
40 #include <sys/cpuhelper.h>
41 #include <sys/module.h>
42 #include <sys/queue.h>
43 #include <sys/serialize.h>
44 #include <sys/sysctl.h>
45 #include <sys/systm.h>
47 #include <machine/specialreg.h>
48 #include <machine/cpufunc.h>
49 #include <machine/cputypes.h>
50 #include <machine/md_var.h>
54 struct clockmod_softc {
55 TAILQ_ENTRY(clockmod_softc) sc_link;
56 struct clockmod_dom *sc_dom;
61 TAILQ_ENTRY(clockmod_dom) dom_link;
62 TAILQ_HEAD(, clockmod_softc) dom_list;
63 struct sysctl_ctx_list dom_sysctl_ctx;
64 struct sysctl_oid *dom_sysctl_tree;
65 cpumask_t dom_cpumask;
71 #define CLOCKMOD_DOM_FLAG_ACTIVE 0x1
73 struct clockmod_dom_ctrl {
78 static int clockmod_dom_attach(struct clockmod_softc *);
79 static void clockmod_dom_detach(struct clockmod_softc *);
80 static struct clockmod_dom *clockmod_dom_find(cpumask_t);
81 static struct clockmod_dom *clockmod_dom_create(cpumask_t);
82 static void clockmod_dom_destroy(struct clockmod_dom *);
84 static int clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS);
85 static int clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS);
86 static int clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS);
88 static void clockmod_identify(driver_t *, device_t);
89 static int clockmod_probe(device_t);
90 static int clockmod_attach(device_t);
91 static int clockmod_detach(device_t);
93 static void clockmod_select_handler(struct cpuhelper_msg *);
94 static int clockmod_select(const struct clockmod_softc *,
95 const struct clockmod_dom_ctrl *);
97 static boolean_t clockmod_errata_duty(int);
99 static struct lwkt_serialize clockmod_dom_slize = LWKT_SERIALIZE_INITIALIZER;
100 static int clockmod_dom_id;
101 static TAILQ_HEAD(, clockmod_dom) clockmod_dom_list =
102 TAILQ_HEAD_INITIALIZER(clockmod_dom_list);
103 static int clockmod_dom_nctrl;
104 static struct clockmod_dom_ctrl *clockmod_dom_controls;
106 static device_method_t clockmod_methods[] = {
107 /* Device interface */
108 DEVMETHOD(device_identify, clockmod_identify),
109 DEVMETHOD(device_probe, clockmod_probe),
110 DEVMETHOD(device_attach, clockmod_attach),
111 DEVMETHOD(device_detach, clockmod_detach),
116 static driver_t clockmod_driver = {
119 sizeof(struct clockmod_softc),
122 static devclass_t clockmod_devclass;
123 DRIVER_MODULE(clockmod, cpu, clockmod_driver, clockmod_devclass, NULL, NULL);
126 clockmod_identify(driver_t *driver, device_t parent)
130 if (device_find_child(parent, "clockmod", -1) != NULL)
133 if (cpu_vendor_id != CPU_VENDOR_INTEL)
136 if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != (CPUID_ACPI | CPUID_TM))
139 child = device_add_child(parent, "clockmod", device_get_unit(parent));
141 device_printf(parent, "add clockmod failed\n");
145 clockmod_probe(device_t dev)
147 device_set_desc(dev, "CPU clock modulation");
152 clockmod_attach(device_t dev)
154 struct clockmod_softc *sc = device_get_softc(dev);
157 sc->sc_cpuid = device_get_unit(dev);
159 error = clockmod_dom_attach(sc);
161 device_printf(dev, "domain attach failed\n");
169 clockmod_detach(device_t dev)
171 clockmod_dom_detach(device_get_softc(dev));
176 clockmod_dom_attach(struct clockmod_softc *sc)
178 struct clockmod_softc *sc1;
179 struct clockmod_dom *dom;
180 cpumask_t mask, found_mask;
183 CPUMASK_ASSZERO(found_mask);
185 mask = get_cpumask_from_level(sc->sc_cpuid, CORE_LEVEL);
186 if (CPUMASK_TESTZERO(mask))
187 CPUMASK_ASSBIT(mask, sc->sc_cpuid);
189 lwkt_serialize_enter(&clockmod_dom_slize);
191 dom = clockmod_dom_find(mask);
193 dom = clockmod_dom_create(mask);
201 TAILQ_INSERT_TAIL(&dom->dom_list, sc, sc_link);
203 TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
204 CPUMASK_ORBIT(found_mask, sc1->sc_cpuid);
206 if (CPUMASK_CMPMASKEQ(found_mask, dom->dom_cpumask)) {
207 /* All cpus in this domain is found */
208 dom->dom_flags |= CLOCKMOD_DOM_FLAG_ACTIVE;
211 lwkt_serialize_exit(&clockmod_dom_slize);
216 clockmod_dom_detach(struct clockmod_softc *sc)
218 struct clockmod_dom *dom;
220 lwkt_serialize_enter(&clockmod_dom_slize);
225 if (dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) {
226 struct clockmod_softc *sc1;
229 TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
230 clockmod_select(sc1, &clockmod_dom_controls[0]);
233 /* One cpu is leaving; domain is no longer active */
234 dom->dom_flags &= ~CLOCKMOD_DOM_FLAG_ACTIVE;
236 TAILQ_REMOVE(&dom->dom_list, sc, sc_link);
237 if (TAILQ_EMPTY(&dom->dom_list))
238 clockmod_dom_destroy(dom);
240 lwkt_serialize_exit(&clockmod_dom_slize);
243 static struct clockmod_dom *
244 clockmod_dom_find(cpumask_t mask)
246 struct clockmod_dom *dom;
248 TAILQ_FOREACH(dom, &clockmod_dom_list, dom_link) {
249 if (CPUMASK_CMPMASKEQ(dom->dom_cpumask, mask))
255 static struct clockmod_dom *
256 clockmod_dom_create(cpumask_t mask)
258 struct clockmod_dom *dom;
261 id = clockmod_dom_id++;
262 dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
264 TAILQ_INIT(&dom->dom_list);
265 dom->dom_cpumask = mask;
266 ksnprintf(dom->dom_name, sizeof(dom->dom_name), "clockmod_dom%d", id);
268 sysctl_ctx_init(&dom->dom_sysctl_ctx);
269 dom->dom_sysctl_tree = SYSCTL_ADD_NODE(&dom->dom_sysctl_ctx,
270 SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, dom->dom_name,
272 if (dom->dom_sysctl_tree == NULL) {
273 kprintf("%s: can't add sysctl node\n", dom->dom_name);
274 kfree(dom, M_DEVBUF);
278 SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
279 SYSCTL_CHILDREN(dom->dom_sysctl_tree),
280 OID_AUTO, "members", CTLTYPE_STRING | CTLFLAG_RD,
281 dom, 0, clockmod_dom_sysctl_members, "A", "member cpus");
283 SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
284 SYSCTL_CHILDREN(dom->dom_sysctl_tree),
285 OID_AUTO, "available", CTLTYPE_STRING | CTLFLAG_RD,
286 dom, 0, clockmod_dom_sysctl_available, "A",
287 "available duty percent");
289 SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
290 SYSCTL_CHILDREN(dom->dom_sysctl_tree),
291 OID_AUTO, "select", CTLTYPE_STRING | CTLFLAG_RW,
292 dom, 0, clockmod_dom_sysctl_select, "A", "select duty");
294 TAILQ_INSERT_TAIL(&clockmod_dom_list, dom, dom_link);
296 if (clockmod_dom_controls == NULL) {
297 int nctrl, step, i, shift, cnt;
300 if (cpu_thermal_feature & CPUID_THERMAL_ECMD)
306 nctrl = 8 << (1 - shift);
307 step = 10000 / nctrl;
309 clockmod_dom_controls =
310 kmalloc(sizeof(struct clockmod_dom_ctrl) * nctrl, M_DEVBUF,
314 kprintf("clock modulation:\n");
317 for (i = 0; i < nctrl; ++i) {
318 struct clockmod_dom_ctrl *ctrl =
319 &clockmod_dom_controls[cnt];
322 duty = 10000 - (i * step);
323 if (clockmod_errata_duty(duty))
327 ksnprintf(ctrl->ctl_name, sizeof(ctrl->ctl_name),
328 "%d.%02d%%", duty / 100, duty % 100);
329 ctrl->ctl_value = (((nctrl - i) << shift) & 0xf);
331 ctrl->ctl_value |= 1 << 4;
334 kprintf(" 0x%04jx %s\n",
335 (uintmax_t)ctrl->ctl_value,
339 clockmod_dom_nctrl = cnt;
345 clockmod_dom_destroy(struct clockmod_dom *dom)
347 KASSERT(TAILQ_EMPTY(&dom->dom_list),
348 ("%s: still has member cpus", dom->dom_name));
349 TAILQ_REMOVE(&clockmod_dom_list, dom, dom_link);
351 sysctl_ctx_free(&dom->dom_sysctl_ctx);
352 kfree(dom, M_DEVBUF);
354 if (TAILQ_EMPTY(&clockmod_dom_list)) {
355 clockmod_dom_nctrl = 0;
356 kfree(clockmod_dom_controls, M_DEVBUF);
357 clockmod_dom_controls = NULL;
362 clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)
364 struct clockmod_dom *dom = arg1;
365 struct clockmod_softc *sc;
368 lwkt_serialize_enter(&clockmod_dom_slize);
371 TAILQ_FOREACH(sc, &dom->dom_list, sc_link) {
374 if (error == 0 && loop)
375 error = SYSCTL_OUT(req, " ", 1);
377 ksnprintf(buf, sizeof(buf), "cpu%d", sc->sc_cpuid);
378 error = SYSCTL_OUT(req, buf, strlen(buf));
383 lwkt_serialize_exit(&clockmod_dom_slize);
388 clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)
390 struct clockmod_dom *dom = arg1;
393 lwkt_serialize_enter(&clockmod_dom_slize);
395 if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
396 error = SYSCTL_OUT(req, " ", 1);
401 for (i = 0; i < clockmod_dom_nctrl; ++i) {
402 if (error == 0 && loop)
403 error = SYSCTL_OUT(req, " ", 1);
405 error = SYSCTL_OUT(req,
406 clockmod_dom_controls[i].ctl_name,
407 strlen(clockmod_dom_controls[i].ctl_name));
412 lwkt_serialize_exit(&clockmod_dom_slize);
417 clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)
419 struct clockmod_dom *dom = arg1;
420 struct clockmod_softc *sc;
421 const struct clockmod_dom_ctrl *ctrl = NULL;
425 lwkt_serialize_enter(&clockmod_dom_slize);
426 KKASSERT(dom->dom_select >= 0 && dom->dom_select < clockmod_dom_nctrl);
427 ksnprintf(duty, sizeof(duty), "%s",
428 clockmod_dom_controls[dom->dom_select].ctl_name);
429 lwkt_serialize_exit(&clockmod_dom_slize);
431 error = sysctl_handle_string(oidp, duty, sizeof(duty), req);
432 if (error != 0 || req->newptr == NULL)
435 lwkt_serialize_enter(&clockmod_dom_slize);
437 if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
442 for (i = 0; i < clockmod_dom_nctrl; ++i) {
443 ctrl = &clockmod_dom_controls[i];
444 if (strcmp(duty, ctrl->ctl_name) == 0)
447 if (i == clockmod_dom_nctrl) {
453 TAILQ_FOREACH(sc, &dom->dom_list, sc_link)
454 clockmod_select(sc, ctrl);
456 lwkt_serialize_exit(&clockmod_dom_slize);
461 clockmod_select_handler(struct cpuhelper_msg *msg)
463 uint64_t ctl_value = *((const uint64_t *)msg->ch_cbarg);
467 kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid,
468 (uintmax_t)ctl_value);
471 wrmsr(MSR_THERM_CONTROL, ctl_value);
472 cpuhelper_replymsg(msg, 0);
476 clockmod_select(const struct clockmod_softc *sc,
477 const struct clockmod_dom_ctrl *ctrl)
479 struct cpuhelper_msg msg;
481 cpuhelper_initmsg(&msg, &curthread->td_msgport,
482 clockmod_select_handler, __DECONST(void *, &ctrl->ctl_value),
484 return (cpuhelper_domsg(&msg, sc->sc_cpuid));
488 clockmod_errata_duty(int duty)
490 uint32_t model, stepping;
493 * This is obtained from the original p4tcc code.
495 * The original errata checking code in p4tcc is obviously wrong.
496 * However, I am no longer being able to find the errata mentioned
497 * in the code. The guess is that the errata only affects family
499 * - The errata applies to only to model 0x00, 0x01 and 0x02 in
500 * the original p4tcc code.
501 * - Software controlled clock modulation has been supported since
502 * 0f_00 and the model of the oldest family 0x06 CPUs supporting
503 * this feature is 0x09.
505 if (CPUID_TO_FAMILY(cpu_id) != 0xf)
508 model = CPUID_TO_MODEL(cpu_id);
509 stepping = cpu_id & 0xf;
516 /* Hang w/ 12.50% and 25.00% */
517 if (duty == 1250 || duty == 2500)
521 } else if (model == 0x2) {
533 } else if (model == 0x1) {
537 /* Hang w/ 12.50% and 25.00% */
538 if (duty == 1250 || duty == 2500)
542 } else if (model == 0x0) {
546 /* Hang w/ 12.50% and 25.00% */
547 if (duty == 1250 || duty == 2500)