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/module.h>
41 #include <sys/queue.h>
42 #include <sys/serialize.h>
43 #include <sys/sysctl.h>
44 #include <sys/systm.h>
46 #include <net/netmsg2.h>
47 #include <net/netisr2.h>
49 #include <machine/specialreg.h>
50 #include <machine/cpufunc.h>
51 #include <machine/cputypes.h>
52 #include <machine/md_var.h>
56 struct netmsg_clockmod {
57 struct netmsg_base base;
61 struct clockmod_softc {
62 TAILQ_ENTRY(clockmod_softc) sc_link;
63 struct clockmod_dom *sc_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;
78 #define CLOCKMOD_DOM_FLAG_ACTIVE 0x1
80 struct clockmod_dom_ctrl {
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 *);
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);
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);
100 static void clockmod_select_handler(netmsg_t);
101 static int clockmod_select(const struct clockmod_softc *,
102 const struct clockmod_dom_ctrl *);
104 static boolean_t clockmod_errata_duty(int);
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;
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),
123 static driver_t clockmod_driver = {
126 sizeof(struct clockmod_softc),
129 static devclass_t clockmod_devclass;
130 DRIVER_MODULE(clockmod, cpu, clockmod_driver, clockmod_devclass, NULL, NULL);
133 clockmod_identify(driver_t *driver, device_t parent)
137 if (device_find_child(parent, "clockmod", -1) != NULL)
140 if (cpu_vendor_id != CPU_VENDOR_INTEL)
143 if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != (CPUID_ACPI | CPUID_TM))
146 child = device_add_child(parent, "clockmod", device_get_unit(parent));
148 device_printf(parent, "add clockmod failed\n");
152 clockmod_probe(device_t dev)
154 device_set_desc(dev, "CPU clock modulation");
159 clockmod_attach(device_t dev)
161 struct clockmod_softc *sc = device_get_softc(dev);
164 sc->sc_cpuid = device_get_unit(dev);
166 error = clockmod_dom_attach(sc);
168 device_printf(dev, "domain attach failed\n");
176 clockmod_detach(device_t dev)
178 clockmod_dom_detach(device_get_softc(dev));
183 clockmod_dom_attach(struct clockmod_softc *sc)
185 struct clockmod_softc *sc1;
186 struct clockmod_dom *dom;
187 cpumask_t mask, found_mask = 0;
190 mask = get_cpumask_from_level(sc->sc_cpuid, CORE_LEVEL);
192 mask = CPUMASK(sc->sc_cpuid);
194 lwkt_serialize_enter(&clockmod_dom_slize);
196 dom = clockmod_dom_find(mask);
198 dom = clockmod_dom_create(mask);
206 TAILQ_INSERT_TAIL(&dom->dom_list, sc, sc_link);
208 TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
209 found_mask |= CPUMASK(sc1->sc_cpuid);
211 if (found_mask == dom->dom_cpumask) {
212 /* All cpus in this domain is found */
213 dom->dom_flags |= CLOCKMOD_DOM_FLAG_ACTIVE;
216 lwkt_serialize_exit(&clockmod_dom_slize);
221 clockmod_dom_detach(struct clockmod_softc *sc)
223 struct clockmod_dom *dom;
225 lwkt_serialize_enter(&clockmod_dom_slize);
230 if (dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) {
231 struct clockmod_softc *sc1;
234 TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
235 clockmod_select(sc1, &clockmod_dom_controls[0]);
238 /* One cpu is leaving; domain is no longer active */
239 dom->dom_flags &= ~CLOCKMOD_DOM_FLAG_ACTIVE;
241 TAILQ_REMOVE(&dom->dom_list, sc, sc_link);
242 if (TAILQ_EMPTY(&dom->dom_list))
243 clockmod_dom_destroy(dom);
245 lwkt_serialize_exit(&clockmod_dom_slize);
248 static struct clockmod_dom *
249 clockmod_dom_find(cpumask_t mask)
251 struct clockmod_dom *dom;
253 TAILQ_FOREACH(dom, &clockmod_dom_list, dom_link) {
254 if (dom->dom_cpumask == mask)
260 static struct clockmod_dom *
261 clockmod_dom_create(cpumask_t mask)
263 struct clockmod_dom *dom;
266 id = clockmod_dom_id++;
267 dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
269 TAILQ_INIT(&dom->dom_list);
270 dom->dom_cpumask = mask;
271 ksnprintf(dom->dom_name, sizeof(dom->dom_name), "clockmod_dom%d", id);
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,
277 if (dom->dom_sysctl_tree == NULL) {
278 kprintf("%s: can't add sysctl node\n", dom->dom_name);
279 kfree(dom, M_DEVBUF);
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");
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");
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");
299 TAILQ_INSERT_TAIL(&clockmod_dom_list, dom, dom_link);
301 if (clockmod_dom_controls == NULL) {
302 int nctrl, step, i, shift, cnt;
305 if (cpu_thermal_feature & CPUID_THERMAL_ECMD)
311 nctrl = 8 << (1 - shift);
312 step = 10000 / nctrl;
314 clockmod_dom_controls =
315 kmalloc(sizeof(struct clockmod_dom_ctrl) * nctrl, M_DEVBUF,
319 kprintf("clock modulation:\n");
322 for (i = 0; i < nctrl; ++i) {
323 struct clockmod_dom_ctrl *ctrl =
324 &clockmod_dom_controls[cnt];
327 duty = 10000 - (i * step);
328 if (clockmod_errata_duty(duty))
332 ksnprintf(ctrl->ctl_name, sizeof(ctrl->ctl_name),
333 "%d.%02d%%", duty / 100, duty % 100);
334 ctrl->ctl_value = (((nctrl - i) << shift) & 0xf);
336 ctrl->ctl_value |= 1 << 4;
339 kprintf(" 0x%04jx %s\n",
340 (uintmax_t)ctrl->ctl_value,
344 clockmod_dom_nctrl = cnt;
350 clockmod_dom_destroy(struct clockmod_dom *dom)
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);
356 sysctl_ctx_free(&dom->dom_sysctl_ctx);
357 kfree(dom, M_DEVBUF);
359 if (TAILQ_EMPTY(&clockmod_dom_list)) {
360 clockmod_dom_nctrl = 0;
361 kfree(clockmod_dom_controls, M_DEVBUF);
362 clockmod_dom_controls = NULL;
367 clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)
369 struct clockmod_dom *dom = arg1;
370 struct clockmod_softc *sc;
373 lwkt_serialize_enter(&clockmod_dom_slize);
376 TAILQ_FOREACH(sc, &dom->dom_list, sc_link) {
379 if (error == 0 && loop)
380 error = SYSCTL_OUT(req, " ", 1);
382 ksnprintf(buf, sizeof(buf), "cpu%d", sc->sc_cpuid);
383 error = SYSCTL_OUT(req, buf, strlen(buf));
388 lwkt_serialize_exit(&clockmod_dom_slize);
393 clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)
395 struct clockmod_dom *dom = arg1;
398 lwkt_serialize_enter(&clockmod_dom_slize);
400 if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
401 error = SYSCTL_OUT(req, " ", 1);
406 for (i = 0; i < clockmod_dom_nctrl; ++i) {
407 if (error == 0 && loop)
408 error = SYSCTL_OUT(req, " ", 1);
410 error = SYSCTL_OUT(req,
411 clockmod_dom_controls[i].ctl_name,
412 strlen(clockmod_dom_controls[i].ctl_name));
417 lwkt_serialize_exit(&clockmod_dom_slize);
422 clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)
424 struct clockmod_dom *dom = arg1;
425 struct clockmod_softc *sc;
426 const struct clockmod_dom_ctrl *ctrl = NULL;
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);
436 error = sysctl_handle_string(oidp, duty, sizeof(duty), req);
437 if (error != 0 || req->newptr == NULL)
440 lwkt_serialize_enter(&clockmod_dom_slize);
442 if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
447 for (i = 0; i < clockmod_dom_nctrl; ++i) {
448 ctrl = &clockmod_dom_controls[i];
449 if (strcmp(duty, ctrl->ctl_name) == 0)
452 if (i == clockmod_dom_nctrl) {
458 TAILQ_FOREACH(sc, &dom->dom_list, sc_link)
459 clockmod_select(sc, ctrl);
461 lwkt_serialize_exit(&clockmod_dom_slize);
466 clockmod_select_handler(netmsg_t msg)
468 struct netmsg_clockmod *cmsg = (struct netmsg_clockmod *)msg;
472 kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid,
473 (uintmax_t)cmsg->ctl_value);
477 wrmsr(MSR_THERM_CONTROL, cmsg->ctl_value);
478 lwkt_replymsg(&cmsg->base.lmsg, 0);
482 clockmod_select(const struct clockmod_softc *sc,
483 const struct clockmod_dom_ctrl *ctrl)
485 struct netmsg_clockmod msg;
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);
494 clockmod_errata_duty(int duty)
496 uint32_t model, stepping;
499 * This is obtained from the original p4tcc code.
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
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.
511 if (CPUID_TO_FAMILY(cpu_id) != 0xf)
514 model = CPUID_TO_MODEL(cpu_id);
515 stepping = cpu_id & 0xf;
529 } else if (model == 0x1) {
533 /* Hang w/ 12.50% and 25.00% */
534 if (duty == 1250 || duty == 2500)
538 } else if (model == 0x0) {
542 /* Hang w/ 12.50% and 25.00% */
543 if (duty == 1250 || duty == 2500)