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/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>
48 #include <machine/specialreg.h>
49 #include <machine/cpufunc.h>
50 #include <machine/cputypes.h>
51 #include <machine/md_var.h>
55 struct clockmod_softc
{
56 TAILQ_ENTRY(clockmod_softc
) sc_link
;
57 struct clockmod_dom
*sc_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
;
72 #define CLOCKMOD_DOM_FLAG_ACTIVE 0x1
74 struct clockmod_dom_ctrl
{
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
*);
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
);
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
);
94 static void clockmod_select_handler(struct cpuhelper_msg
*);
95 static int clockmod_select(const struct clockmod_softc
*,
96 const struct clockmod_dom_ctrl
*);
98 static boolean_t
clockmod_errata_duty(int);
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
;
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
),
117 static driver_t clockmod_driver
= {
120 sizeof(struct clockmod_softc
),
123 static devclass_t clockmod_devclass
;
124 DRIVER_MODULE(clockmod
, cpu
, clockmod_driver
, clockmod_devclass
, NULL
, NULL
);
127 clockmod_identify(driver_t
*driver
, device_t parent
)
131 if (device_find_child(parent
, "clockmod", -1) != NULL
)
134 if (cpu_vendor_id
!= CPU_VENDOR_INTEL
)
137 if ((cpu_feature
& (CPUID_ACPI
| CPUID_TM
)) != (CPUID_ACPI
| CPUID_TM
))
140 child
= device_add_child(parent
, "clockmod", device_get_unit(parent
));
142 device_printf(parent
, "add clockmod failed\n");
146 clockmod_probe(device_t dev
)
148 device_set_desc(dev
, "CPU clock modulation");
153 clockmod_attach(device_t dev
)
155 struct clockmod_softc
*sc
= device_get_softc(dev
);
158 sc
->sc_cpuid
= device_get_unit(dev
);
160 error
= clockmod_dom_attach(sc
);
162 device_printf(dev
, "domain attach failed\n");
170 clockmod_detach(device_t dev
)
172 clockmod_dom_detach(device_get_softc(dev
));
177 clockmod_dom_attach(struct clockmod_softc
*sc
)
179 struct clockmod_softc
*sc1
;
180 struct clockmod_dom
*dom
;
181 cpumask_t mask
, found_mask
;
184 CPUMASK_ASSZERO(found_mask
);
186 mask
= get_cpumask_from_level(sc
->sc_cpuid
, CORE_LEVEL
);
187 if (CPUMASK_TESTZERO(mask
))
188 CPUMASK_ASSBIT(mask
, sc
->sc_cpuid
);
190 lwkt_serialize_enter(&clockmod_dom_slize
);
192 dom
= clockmod_dom_find(mask
);
194 dom
= clockmod_dom_create(mask
);
202 TAILQ_INSERT_TAIL(&dom
->dom_list
, sc
, sc_link
);
204 TAILQ_FOREACH(sc1
, &dom
->dom_list
, sc_link
)
205 CPUMASK_ORBIT(found_mask
, sc1
->sc_cpuid
);
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
;
212 lwkt_serialize_exit(&clockmod_dom_slize
);
217 clockmod_dom_detach(struct clockmod_softc
*sc
)
219 struct clockmod_dom
*dom
;
221 lwkt_serialize_enter(&clockmod_dom_slize
);
226 if (dom
->dom_flags
& CLOCKMOD_DOM_FLAG_ACTIVE
) {
227 struct clockmod_softc
*sc1
;
230 TAILQ_FOREACH(sc1
, &dom
->dom_list
, sc_link
)
231 clockmod_select(sc1
, &clockmod_dom_controls
[0]);
234 /* One cpu is leaving; domain is no longer active */
235 dom
->dom_flags
&= ~CLOCKMOD_DOM_FLAG_ACTIVE
;
237 TAILQ_REMOVE(&dom
->dom_list
, sc
, sc_link
);
238 if (TAILQ_EMPTY(&dom
->dom_list
))
239 clockmod_dom_destroy(dom
);
241 lwkt_serialize_exit(&clockmod_dom_slize
);
244 static struct clockmod_dom
*
245 clockmod_dom_find(cpumask_t mask
)
247 struct clockmod_dom
*dom
;
249 TAILQ_FOREACH(dom
, &clockmod_dom_list
, dom_link
) {
250 if (CPUMASK_CMPMASKEQ(dom
->dom_cpumask
, mask
))
256 static struct clockmod_dom
*
257 clockmod_dom_create(cpumask_t mask
)
259 struct clockmod_dom
*dom
;
262 id
= clockmod_dom_id
++;
263 dom
= kmalloc(sizeof(*dom
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
265 TAILQ_INIT(&dom
->dom_list
);
266 dom
->dom_cpumask
= mask
;
267 ksnprintf(dom
->dom_name
, sizeof(dom
->dom_name
), "clockmod_dom%d", id
);
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
,
273 if (dom
->dom_sysctl_tree
== NULL
) {
274 kprintf("%s: can't add sysctl node\n", dom
->dom_name
);
275 kfree(dom
, M_DEVBUF
);
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");
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");
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");
295 TAILQ_INSERT_TAIL(&clockmod_dom_list
, dom
, dom_link
);
297 if (clockmod_dom_controls
== NULL
) {
298 int nctrl
, step
, i
, shift
, cnt
;
301 if (cpu_thermal_feature
& CPUID_THERMAL_ECMD
)
307 nctrl
= 8 << (1 - shift
);
308 step
= 10000 / nctrl
;
310 clockmod_dom_controls
=
311 kmalloc(sizeof(struct clockmod_dom_ctrl
) * nctrl
, M_DEVBUF
,
315 kprintf("clock modulation:\n");
318 for (i
= 0; i
< nctrl
; ++i
) {
319 struct clockmod_dom_ctrl
*ctrl
=
320 &clockmod_dom_controls
[cnt
];
323 duty
= 10000 - (i
* step
);
324 if (clockmod_errata_duty(duty
))
328 ksnprintf(ctrl
->ctl_name
, sizeof(ctrl
->ctl_name
),
329 "%d.%02d%%", duty
/ 100, duty
% 100);
330 ctrl
->ctl_value
= (((nctrl
- i
) << shift
) & 0xf);
332 ctrl
->ctl_value
|= 1 << 4;
335 kprintf(" 0x%04jx %s\n",
336 (uintmax_t)ctrl
->ctl_value
,
340 clockmod_dom_nctrl
= cnt
;
346 clockmod_dom_destroy(struct clockmod_dom
*dom
)
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
);
352 sysctl_ctx_free(&dom
->dom_sysctl_ctx
);
353 kfree(dom
, M_DEVBUF
);
355 if (TAILQ_EMPTY(&clockmod_dom_list
)) {
356 clockmod_dom_nctrl
= 0;
357 kfree(clockmod_dom_controls
, M_DEVBUF
);
358 clockmod_dom_controls
= NULL
;
363 clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS
)
365 struct clockmod_dom
*dom
= arg1
;
366 struct clockmod_softc
*sc
;
369 lwkt_serialize_enter(&clockmod_dom_slize
);
372 TAILQ_FOREACH(sc
, &dom
->dom_list
, sc_link
) {
375 if (error
== 0 && loop
)
376 error
= SYSCTL_OUT(req
, " ", 1);
378 ksnprintf(buf
, sizeof(buf
), "cpu%d", sc
->sc_cpuid
);
379 error
= SYSCTL_OUT(req
, buf
, strlen(buf
));
384 lwkt_serialize_exit(&clockmod_dom_slize
);
389 clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS
)
391 struct clockmod_dom
*dom
= arg1
;
394 lwkt_serialize_enter(&clockmod_dom_slize
);
396 if ((dom
->dom_flags
& CLOCKMOD_DOM_FLAG_ACTIVE
) == 0) {
397 error
= SYSCTL_OUT(req
, " ", 1);
402 for (i
= 0; i
< clockmod_dom_nctrl
; ++i
) {
403 if (error
== 0 && loop
)
404 error
= SYSCTL_OUT(req
, " ", 1);
406 error
= SYSCTL_OUT(req
,
407 clockmod_dom_controls
[i
].ctl_name
,
408 strlen(clockmod_dom_controls
[i
].ctl_name
));
413 lwkt_serialize_exit(&clockmod_dom_slize
);
418 clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS
)
420 struct clockmod_dom
*dom
= arg1
;
421 struct clockmod_softc
*sc
;
422 const struct clockmod_dom_ctrl
*ctrl
= NULL
;
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
);
432 error
= sysctl_handle_string(oidp
, duty
, sizeof(duty
), req
);
433 if (error
!= 0 || req
->newptr
== NULL
)
436 lwkt_serialize_enter(&clockmod_dom_slize
);
438 if ((dom
->dom_flags
& CLOCKMOD_DOM_FLAG_ACTIVE
) == 0) {
443 for (i
= 0; i
< clockmod_dom_nctrl
; ++i
) {
444 ctrl
= &clockmod_dom_controls
[i
];
445 if (strcmp(duty
, ctrl
->ctl_name
) == 0)
448 if (i
== clockmod_dom_nctrl
) {
454 TAILQ_FOREACH(sc
, &dom
->dom_list
, sc_link
)
455 clockmod_select(sc
, ctrl
);
457 lwkt_serialize_exit(&clockmod_dom_slize
);
462 clockmod_select_handler(struct cpuhelper_msg
*msg
)
464 uint64_t ctl_value
= *((const uint64_t *)msg
->ch_cbarg
);
468 kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid
,
469 (uintmax_t)ctl_value
);
472 wrmsr(MSR_THERM_CONTROL
, ctl_value
);
473 cpuhelper_replymsg(msg
, 0);
477 clockmod_select(const struct clockmod_softc
*sc
,
478 const struct clockmod_dom_ctrl
*ctrl
)
480 struct cpuhelper_msg msg
;
482 cpuhelper_initmsg(&msg
, &curthread
->td_msgport
,
483 clockmod_select_handler
, __DECONST(void *, &ctrl
->ctl_value
),
485 return (cpuhelper_domsg(&msg
, sc
->sc_cpuid
));
489 clockmod_errata_duty(int duty
)
491 uint32_t model
, stepping
;
494 * This is obtained from the original p4tcc code.
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
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.
506 if (CPUID_TO_FAMILY(cpu_id
) != 0xf)
509 model
= CPUID_TO_MODEL(cpu_id
);
510 stepping
= cpu_id
& 0xf;
517 /* Hang w/ 12.50% and 25.00% */
518 if (duty
== 1250 || duty
== 2500)
522 } else if (model
== 0x2) {
534 } else if (model
== 0x1) {
538 /* Hang w/ 12.50% and 25.00% */
539 if (duty
== 1250 || duty
== 2500)
543 } else if (model
== 0x0) {
547 /* Hang w/ 12.50% and 25.00% */
548 if (duty
== 1250 || duty
== 2500)